June 13, 2007 at 9:43 am
Hi, I am trying to use a SP to get me the sales of certain products, returns of the same products by store and in total. I have manged to write something that is clearly innefficient as it would mean I would have to enter each store id (e.g. 2007, 2065) to get my answer. Anyone know of a better way of writing it? Help is much appreciated:
CREATE PROCEDURE [dbo].[Returns_Report_Non_Tols]
@lowerdate smalldatetime,
@upperdate smalldatetime
AS
BEGIN
select stores.name, stores.storeid,
(select Distinct count (orders.orderid) as 'Sales'
from
stores left join orders on stores.storeid = orders.oaorstoreid
left join orderdetails on orders.orderid = orderdetails.orderid
left join products on orderdetails.productid = products.productid
where
Orders.OrderCancel <> 'Y' and
Orders.OrderDate between @lowerdate and @upperdate and
(Products.ProductID = '00154142-4'
or Products.ProductID = '00195913-2'
or Products.ProductID = '00471206-8'
or Products.ProductID = '00534207-2'
or Products.ProductID = '00660681-3'
or Products.ProductID = '00825717-4'
or Products.ProductID = '98765432-9'
or Products.ProductID = 'SKVF')) as 'TotalSales',
(select Distinct count (orders.orderid) as 'Sales'
from
stores left join orders on stores.storeid = orders.oaorstoreid
left join orderdetails on orders.orderid = orderdetails.orderid
left join products on orderdetails.productid = products.productid
where
Orders.Oaorstoreid = '2007' and
Orders.OrderCancel <> 'Y' and
Orders.OrderDate between @lowerdate and @upperdate and
(Products.ProductID = '00154142-4'
or Products.ProductID = '00195913-2'
or Products.ProductID = '00471206-8'
or Products.ProductID = '00534207-2'
or Products.ProductID = '00660681-3'
or Products.ProductID = '00825717-4'
or Products.ProductID = '98765432-9'
or Products.ProductID = 'SKVF')) as '2007Sales',
(select Distinct count (orders.orderid) as 'Sales'
from
stores left join orders on stores.storeid = orders.oaorstoreid
left join orderdetails on orders.orderid = orderdetails.orderid
left join products on orderdetails.productid = products.productid
where
Orders.Oaorstoreid = '2065' and
Orders.OrderCancel <> 'Y' and
Orders.OrderDate between @lowerdate and @upperdate and
(Products.ProductID = '00154142-4'
or Products.ProductID = '00195913-2'
or Products.ProductID = '00471206-8'
or Products.ProductID = '00534207-2'
or Products.ProductID = '00660681-3'
or Products.ProductID = '00825717-4'
or Products.ProductID = '98765432-9'
or Products.ProductID = 'SKVF')) as '2065Sales',
(select Distinct count (storereturns.frames_orderid) as 'Returns'
from
stores left join orders on stores.storeid = orders.oaorstoreid
left join orderdetails on orders.orderid = orderdetails.orderid
left join products on orderdetails.productid = products.productid
left join storereturns on orders.orderid = storereturns.frames_orderid
left join returnscode on storereturns.returnscode = returnscode.returnscode
where
returnscode.returnscode = 'T1' and
StoreReturns.ReturnsDate between @lowerdate and @upperdate and
(Products.ProductID = '00154142-4'
or Products.ProductID = '00195913-2'
or Products.ProductID = '00471206-8'
or Products.ProductID = '00534207-2'
or Products.ProductID = '00660681-3'
or Products.ProductID = '00825717-4'
or Products.ProductID = '98765432-9'
or Products.ProductID = 'SKVF')) as 'TotalReturns',
(select Distinct count (storereturns.frames_orderid) as 'Returns'
from
stores left join orders on stores.storeid = orders.oaorstoreid
left join orderdetails on orders.orderid = orderdetails.orderid
left join products on orderdetails.productid = products.productid
left join storereturns on orders.orderid = storereturns.frames_orderid
left join returnscode on storereturns.returnscode = returnscode.returnscode
where
returnscode.returnscode = 'T1' and
StoreReturns.storeid = '2007' and
StoreReturns.ReturnsDate between @lowerdate and @upperdate and
(Products.ProductID = '00154142-4'
or Products.ProductID = '00195913-2'
or Products.ProductID = '00471206-8'
or Products.ProductID = '00534207-2'
or Products.ProductID = '00660681-3'
or Products.ProductID = '00825717-4'
or Products.ProductID = '98765432-9'
or Products.ProductID = 'SKVF')) as '2007Returns',
(select Distinct count (storereturns.frames_orderid) as 'Returns'
from
stores left join orders on stores.storeid = orders.oaorstoreid
left join orderdetails on orders.orderid = orderdetails.orderid
left join products on orderdetails.productid = products.productid
left join storereturns on orders.orderid = storereturns.frames_orderid
left join returnscode on storereturns.returnscode = returnscode.returnscode
where
returnscode.returnscode = 'T1' and
StoreReturns.storeid = '2065' and
StoreReturns.ReturnsDate between @lowerdate and @upperdate and
(Products.ProductID = '00154142-4'
or Products.ProductID = '00195913-2'
or Products.ProductID = '00471206-8'
or Products.ProductID = '00534207-2'
or Products.ProductID = '00660681-3'
or Products.ProductID = '00825717-4'
or Products.ProductID = '98765432-9'
or Products.ProductID = 'SKVF')) as '2065Returns'
from
stores
where stores.opticianstore_flag = 'Y'
End
GO
June 13, 2007 at 10:30 am
Nadeem
You haven't really provided enough information. Do you always need the results from the same set of stores, or is it different each time? If the stores vary, then is it two stores all the time, or does the number of stores vary as well?
One thing I have noticed in your query is that you're using a LEFT JOIN and then imposing a filter (WHERE clause) on the right-hand table: this effectively turns your LEFT JOIN into an INNER JOIN. You should rewrite your query, changing LEFT JOINs to INNER JOINs where appropriate.
Also, do you really need to join to Products at all? You could rewrite that part of your WHERE clause as Orderdetails.ProductID = ''00154142-4' and so on, couldn't you?
John
June 13, 2007 at 11:07 am
Try this (Though I have made a lot of assumptions about your data, including that you really didn't need to do a lot of the joins you were doing to get the appropriate data):
if object_id('returns_report_non_tols') is not null drop procedure returns_report_non_tols;
go
CREATE PROCEDURE [dbo].[Returns_Report_Non_Tols] @lowerdate smalldatetime, @upperdate smalldatetime
AS
--Get all the order data we want to work with into a temp table
select o.orderid,
o.orderdate,
o.oaorstoreid as storeid,
od.productid
into #tmpOrders
from orders o join ordersdetail od on (o.orderid = od.orderid)
where o.ordercancel <> 'Y' and
o.orderdate between @lowerdate and @upperdate and
od.productid in ('00154142-4','00195913-2','00471206-8','00534207-2','00660681-3','00825717-4','98765432-9','SKVF')
--Get all the applicable return data that we want to work with into a temp table
--NOTE: I don't know your data well enough so you need to validate this one.
select o.orderid,
o.orderdate,
o.oaorstoreid as storeid,
od.productid,
sr.returnsdate
into #tmpReturns
from orders o join ordersdetail od on (o.orderid = od.orderid)
join storereturns sr on (sr.frames_orderid = o.orderid) --I'm assuming two stores can not have the same orderid otherwise we need to qualify this with the storeid
where o.ordercancel <> 'Y' and
od.productid in ('00154142-4','00195913-2','00471206-8','00534207-2','00660681-3','00825717-4','98765432-9','SKVF') and
sr.returnsdate between @lowerdate and @upperdate and
sr.returnscode = 'T1'
--now using our temp tables we can create a singe select against
--the stores table and a couple of computed tables using our temp tables and
--grouping on the storeid
select s.storeid, TotalSales, TotalReturns
from (
select storeid, count(*) as TotalSales
from #tmpOrders o
group by storeid
) ts join
(
select storeid, count(*) as TotalReturns
from #tmpReturns r
group by storeid
) tr on ts.storeid = tr.storeid
join stores s on s.storeid = ts.storeid
where s.opticianstore_flag = 'Y'
--Now you get one row per store with Total Sales and Total Returns.
--Of course I'm assuming that all stores will have at least one sale and one return
-- otherwise you need to account for missing data (outer joins, etc)
--Now if only I had some test data to verify I didn't do anything stupid.
--NOTE: If you really need just one row with the stores Sale/Return data pivoted, well that is another story. As it stands I'm sure someone smarter and with more time could get this down to a single select statement eliminating the temp tables.
--James.
go
June 13, 2007 at 11:23 am
The original query returns one row for each store, then does subqueries returning a totals column for each store in every row. Surely that can't be what you want. Also, the joins to products and returnscode appear to be unnecessary.
I think you'll get inaccurate counts with the previous solution. If there are multiple products on the target list in the same order, you'll count the same orderid more than once, so you still need to do a SELECT DISTINCT on the orderdetails table. Here's my version:
select
stores.name, stores.storeid,
sum(case when Orders.OrderDate between @lowerdate and @upperdate then 1 else 0 end) as TotalSales,
count(storereturns.frames_orderid) as [Returns]
from stores
left join orders
inner join (
select distinct orderid
from orderdetails
where productid IN ('00154142-4', '00195913-2', '00471206-8', '00534207-2', '00660681-3', '00825717-4', '98765432-9', 'SKVF')
) orderdetails on orders.orderid = orderdetails.orderid
on stores.storeid = orders.oaorstoreid
left join storereturns on orders.orderid = storereturns.frames_orderid
and storereturns.returnscode = 'T1'
and StoreReturns.ReturnsDate between @lowerdate and @upperdate
where stores.opticianstore_flag = 'Y'
and Orders.OrderCancel <> 'Y'
and (Orders.OrderDate between @lowerdate and @upperdate OR storereturns.frames_orderid IS NOT NULL)
group by stores.name, stores.storeid
June 14, 2007 at 4:12 am
Thanks for all your input.
John was correct that Product table was no longer required (I had used it earlier, but didn't need it any longer).
Scott - your solution looks perfect! Doing some figures on it using Crystal Reports and it's looking good.
Thanks again.
Viewing 5 posts - 1 through 4 (of 4 total)
You must be logged in to reply to this topic. Login to reply