TSQL calculating from combined queries - sql

I currently have six queries in which I take the results and use a spreadsheet to calculate two different final percentages. I believe that it can be done in a single query, and without a spreadsheet, but I am not knowledgeable enough in SQL to figure it out. I am hoping for some direction from the amazing SQL Gods here on SO.
We have several locations, and calculate a Past Due % and a Past Due Fallout %, per location, based on the average of two other percentages:
Past Due Dollars ÷ Projected Dollars = Past Due Float %
Past Due Units ÷ Total Active Units = Past Due Unit %
( Past Due Unit % + Past Due Dollar % ) / 2 = Past Due %
Fallout uses the same calculations, but looks at what the amounts will be tomorrow.
SOLVED: I spent time learning about sub-queries, and joined them by the STID. Thanks for all who assisted and helped guide me in the
correct direction.
Here is my final code:
SET DATEFIRST 1;
DECLARE #Today date = dbo.getdateparam(92,999);
DECLARE #TodayNum int = DATEPART(dw, #Today);
DECLARE #Saturday date = DATEADD(DAY, (6-#TodayNum)%7, #Today);
DECLARE #PrevSat date = DATEADD(DAY, -7, #Saturday);
Select store.STID As Store,
Proj.ProjRent As Projected,
PDRent.PastDueDollars As PDRent,
UOR.Units As UOR,
PDUnits.UnitsPD As PDUnits,
(PDRent.PastDueDollars / Proj.ProjRent) * 100 As FloatPerc,
(Cast(PDUnits.UnitsPD As Decimal) / Cast(UOR.Units As Decimal)) *
100 As UnitPerc,
Cast(Round((((PDRent.PastDueDollars / Proj.ProjRent) * 100) +
((Cast(PDUnits.UnitsPD As Decimal(18,4)) / Cast(UOR.Units As Decimal(18,4))) *
100)) / 2, 2) As Decimal(18,2)) As PDPerc,
Reds.RedsPD As PDReds,
Round(Cast(Reds.RedsPD As Float) / Cast(UOR.Units As Float) * 100,
2) As RedsPerc
From
-- Stores
(Select Distinct Stores.STID,
Stores.StoreName,
Stores.EMail,
Stores.ManagersName
From Stores
Where Stores.STID Not In (7, 999)) As store
-- Projected Rent
Left Join (Select CashProj.STID,
Sum(CashProj.ProjectedRental) As ProjRent
From CashProj
Where CashProj.ProjectionDate Between DateAdd(mm, DateDiff(mm, 0, #Today),
0) And DateAdd(mm, DateDiff(mm, 0, #Today) + 1, 0)
Group By CashProj.STID) As Proj On store.STID = Proj.STID
-- Past Due Float
Left Join (Select Agreemnt.STID As STID,
Sum(DateDiff(d, Agreemnt.DueDate, (Case DatePart(dw, #Today)
When '1' Then DateAdd(DAY, -7, DateAdd(DAY, (6 - DatePart(dw,
#Today)) % 7, #Today)) When '6' Then #Today
Else DateAdd(DAY, (6 - DatePart(dw, #Today)) % 7, #Today)
End)) * Round(Agreemnt.WeeklyRate / 7, 2)) As PastDueDollars,
DatePart(dw, #Today) As TodayNum,
DateAdd(DAY, -7, DateAdd(DAY, (6 - DatePart(dw, #Today)) % 7,
#Today)) As PrevSat,
DateAdd(DAY, (6 - DatePart(dw, #Today)) % 7, #Today) As Saturday
From Agreemnt
Where Agreemnt.AStatID = 1 And Agreemnt.DueDate < (Case DatePart(dw,
#Today)
When '1' Then DateAdd(DAY, -7, DateAdd(DAY, (6 - DatePart(dw,
#Today)) % 7, #Today)) When '6' Then #Today
Else DateAdd(DAY, (6 - DatePart(dw, #Today)) % 7, #Today)
End) And Agreemnt.RentToRent = 0
Group By Agreemnt.STID) As PDRent On store.STID = PDRent.STID
-- Units On Rent
Left Join (Select Invntry.STID,
Cast(Count(Invntry.StockNumber) As Int) As Units
From Invntry
Inner Join AgreHist On Invntry.InvID = AgreHist.InvID And
Invntry.STID = AgreHist.STID
Inner Join Agreemnt On Agreemnt.STID = AgreHist.STID And
Agreemnt.AgreeID = AgreHist.AgreeID And Agreemnt.AStatID =
AgreHist.AStatID
Where Invntry.InvStatID = 11 And Invntry.DisposalDate Is Null And
Agreemnt.AStatID = 1
Group By Invntry.STID) As UOR On store.STID = UOR.STID
-- Past Due Units
Left Join (Select Invntry.STID,
Cast(Count(Invntry.StockNumber) As Int) As UnitsPD
From Invntry
Inner Join AgreHist On Invntry.InvID = AgreHist.InvID And
Invntry.STID = AgreHist.STID
Inner Join Agreemnt On Agreemnt.STID = AgreHist.STID And
Agreemnt.AgreeID = AgreHist.AgreeID And AgreHist.AStatID =
Agreemnt.AStatID
Where Invntry.InvStatID = 11 And Invntry.DisposalDate Is Null And
Agreemnt.AStatID = 1 And Agreemnt.DueDate < (Case #TodayNum When '1' Then #PrevSat When '6' Then #Today Else #Saturday End) And Agreemnt.RentToRent = 0
Group By Invntry.STID) As PDUnits On store.STID = PDUnits.STID
-- Reds
Left Join (Select Invntry.STID,
Count(Invntry.StockNumber) As RedsPD
From Invntry
Inner Join AgreHist On Invntry.InvID = AgreHist.InvID And
Invntry.STID = AgreHist.STID
Inner Join Agreemnt On Agreemnt.STID = AgreHist.STID And
Agreemnt.AgreeID = AgreHist.AgreeID And Agreemnt.AStatID =
AgreHist.AStatID
Where Invntry.InvStatID = 11 And Invntry.DisposalDate Is Null And
Agreemnt.AStatID = 1 And Agreemnt.DueDate < DateAdd(day, -15, Case
Cast(DatePart(dw, #Today) As Int)
When '1' Then Cast(DateAdd(DAY, -7, DateAdd(DAY, (6 - DatePart(dw,
#Today)) % 7, #Today)) As Date)
When '6' Then Cast(#Today As Date)
Else Cast(DateAdd(DAY, (6 - DatePart(dw, #Today)) % 7, #Today) As
Date) End) And Agreemnt.RentToRent = 0
Group By Invntry.STID) As Reds On store.STID = Reds.STID
Order By Store

You can't use variables like that and you can't do this in one aggregate query because it's going to create a massive Cartesian product and give you incorrect results.
You could use CTE's or subqueries for each query you have listed and then join them together on the STID and apply your formulas.
example...
/* declare all variables here */
DECLARE #dayNum INT;
SET #dayNum = datepart(dw, getdate());
with PastDueDollars as (
select ... from ... group by STID
), ProjectedDollers as (
select ... from ... group by STID
), PastDueUnits as (
select ... from ... group by STID
), preFinal as (
select
PastDueDollarRatio = pdd.PastDueDollars / pd.ProjRent,
PastDueUnitRatio = pdu.UnitsPD / tau.TotalActiveUnits
/* add the other calculations */
from
PastDueDollars pdd
inner join ProjectedDollers pd on pdd.STID = pd.STID
inner join PastDueUnits pdu on pdu.STID = pd.STID
/* join more CTEs */
)
select
f.*,
PastDueRatio = (f.PastDueDollarRatio + f.PastDueUnitRatio) / 2
/* and so on for the other calculations of calculations... */
from
preFinal f

You never set the variables to any real value, the you proceed to select the useless variable in a irrelevant SELECT statement.
in the following line
Set #pdD = Sum( Case When a.DueDate < GetDate() Then DateDiff(d,a.DueDate,#runDate) * (a.WeeklyRate/7) )
You are not stating where s.DueDate comes from. It wont even compile.
In this SELECT the table is completely irrelevant
Select a.STID as STID,
#pdU As PastDueUnits,
#activeU As ActiveUnits,
#pdD As PastDueDollars,
#projRent As ProjRent,
#pdP As PastDuePerc,
#foU As FalloutUnits,
#foD As FalloutDollars,
#foP As FalloutPerc
FROM Agreemnt a INNER JOIN CashProj on a.STID = CashProj.STID JOIN Invntry i ON a.STID = i.STID JOIN AgreHist h On i.InvID = h.InvID And i.STID = h.STID INNER JOIN Agreemnt a On a.STID = h.STID AND a.AgreeID = h.AgreeID AND a.AStatID = h.AStatID
WHERE a.RentToRent = 0 AND i.InvStatID = 11 AND i.DisposalDate IS NULL AND a.AStatID = 1 AND a.DueDate < DateAdd(d, #runDate, GetDate())
GROUP BY a.STID
ORDER BY a.STID
This is an example to how you should set the values before you try to do calculations with the variables.
DECLARE #dayNum INT;
SET #dayNum = datepart(dw, getdate());
Select Invntry.STID,
#foU = COUNT(Invntry.StockNumber)
From Invntry
Inner Join AgreHist On Invntry.InvID = AgreHist.InvID And
Invntry.STID = AgreHist.STID
Inner Join Agreemnt On Agreemnt.STID = AgreHist.STID And Agreemnt.AgreeID =
AgreHist.AgreeID And Agreemnt.AStatID = AgreHist.AStatID
Where Invntry.InvStatID = 11 And Invntry.DisposalDate Is Null And
Agreemnt.AStatID = 1 And Agreemnt.DueDate < DateAdd(d, Case #dayNum When '1' Then -2 When '2' Then 1 When '3' Then 1 When '4' Then 1
When '5' Then 1 When '6' Then 0 When '7' Then -1 End, GetDate()) And Agreemnt.RentToRent = 0
Group By Invntry.STID

My problem was a lack of knowledge and understanding of SQL, which I'm improving on. Here is what I ended up with that gave me the result I desired, whether the most efficient or not.
SET DATEFIRST 1;
DECLARE #Today date = dbo.getdateparam(92,999);
DECLARE #TodayNum int = DATEPART(dw, #Today);
DECLARE #Saturday date = DATEADD(DAY, (6-#TodayNum)%7, #Today);
DECLARE #PrevSat date = DATEADD(DAY, -7, #Saturday);
Select store.STID As Store,
Proj.ProjRent As Projected,
PDRent.PastDueDollars As PDRent,
UOR.Units As UOR,
PDUnits.UnitsPD As PDUnits,
(PDRent.PastDueDollars / Proj.ProjRent) * 100 As FloatPerc,
(Cast(PDUnits.UnitsPD As Decimal) / Cast(UOR.Units As Decimal)) * 100 As UnitPerc,
Cast(Round((((PDRent.PastDueDollars / Proj.ProjRent) * 100) + ((Cast(PDUnits.UnitsPD As Decimal(18,4)) / Cast(UOR.Units As Decimal(18,4))) * 100)) / 2, 2) As Decimal(18,2)) As PDPerc,
Reds.RedsPD As PDReds,
Round(Cast(Reds.RedsPD As Float) / Cast(UOR.Units As Float) * 100,2) As RedsPerc
From
-- Stores
(Select Distinct Stores.STID,
Stores.StoreName,
Stores.EMail,
Stores.ManagersName
From Stores
Where Stores.STID Not In (7, 999)) As store
-- Projected Rent
Left Join (Select CashProj.STID,
Sum(CashProj.ProjectedRental) As ProjRent
From CashProj
Where CashProj.ProjectionDate Between DateAdd(mm, DateDiff(mm, 0, #Today),0) And DateAdd(mm, DateDiff(mm, 0, #Today) + 1, 0)
Group By CashProj.STID) As Proj On store.STID = Proj.STID
-- Past Due Float
Left Join (Select Agreemnt.STID As STID,
Sum(DateDiff(d, Agreemnt.DueDate, (Case DatePart(dw, #Today) When '1' Then DateAdd(DAY, -7, DateAdd(DAY, (6 - DatePart(dw, #Today)) % 7, #Today)) When '6' Then #Today
Else DateAdd(DAY, (6 - DatePart(dw, #Today)) % 7, #Today) End)) * Round(Agreemnt.WeeklyRate / 7, 2)) As PastDueDollars,
DatePart(dw, #Today) As TodayNum,
DateAdd(DAY, -7, DateAdd(DAY, (6 - DatePart(dw, #Today)) % 7,
#Today)) As PrevSat,
DateAdd(DAY, (6 - DatePart(dw, #Today)) % 7, #Today) As Saturday
From Agreemnt
Where Agreemnt.AStatID = 1 And Agreemnt.DueDate < (Case DatePart(dw, #Today) When '1' Then DateAdd(DAY, -7, DateAdd(DAY, (6 - DatePart(dw, #Today)) % 7, #Today)) When '6' Then #Today Else DateAdd(DAY, (6 - DatePart(dw, #Today)) % 7, #Today) End) And Agreemnt.RentToRent = 0
Group By Agreemnt.STID) As PDRent On store.STID = PDRent.STID
-- Units On Rent
Left Join (Select Invntry.STID,
Cast(Count(Invntry.StockNumber) As Int) As Units
From Invntry
Inner Join AgreHist On Invntry.InvID = AgreHist.InvID And
Invntry.STID = AgreHist.STID
Inner Join Agreemnt On Agreemnt.STID = AgreHist.STID And
Agreemnt.AgreeID = AgreHist.AgreeID And Agreemnt.AStatID =
AgreHist.AStatID
Where Invntry.InvStatID = 11 And Invntry.DisposalDate Is Null And Agreemnt.AStatID = 1
Group By Invntry.STID) As UOR On store.STID = UOR.STID
-- Past Due Units
Left Join (Select Invntry.STID,
Cast(Count(Invntry.StockNumber) As Int) As UnitsPD
From Invntry
Inner Join AgreHist On Invntry.InvID = AgreHist.InvID And
Invntry.STID = AgreHist.STID
Inner Join Agreemnt On Agreemnt.STID = AgreHist.STID And
Agreemnt.AgreeID = AgreHist.AgreeID And AgreHist.AStatID =
Agreemnt.AStatID
Where Invntry.InvStatID = 11 And Invntry.DisposalDate Is Null And
Agreemnt.AStatID = 1 And Agreemnt.DueDate < (Case #TodayNum When '1' Then #PrevSat When '6' Then #Today Else #Saturday End) And Agreemnt.RentToRent = 0
Group By Invntry.STID) As PDUnits On store.STID = PDUnits.STID
-- Reds
Left Join (Select Invntry.STID,
Count(Invntry.StockNumber) As RedsPD
From Invntry
Inner Join AgreHist On Invntry.InvID = AgreHist.InvID And Invntry.STID = AgreHist.STID
Inner Join Agreemnt On Agreemnt.STID = AgreHist.STID And Agreemnt.AgreeID = AgreHist.AgreeID And Agreemnt.AStatID = AgreHist.AStatID
Where Invntry.InvStatID = 11 And Invntry.DisposalDate Is Null And Agreemnt.AStatID = 1 And Agreemnt.DueDate < DateAdd(day, -15, Case Cast(DatePart(dw, #Today) As Int) When '1' Then Cast(DateAdd(DAY, -7, DateAdd(DAY, (6 - DatePart(dw, #Today)) % 7, #Today)) As Date) When '6' Then Cast(#Today As Date) Else Cast(DateAdd(DAY, (6 - DatePart(dw, #Today)) % 7, #Today) As Date) End) And Agreemnt.RentToRent = 0
Group By Invntry.STID) As Reds On store.STID = Reds.STID
Order By Store

Related

Function to exclude Saturdays and Sundays

I created a "Daily Sales Query" that captures all the Total Sales entered from the previous work day which runs Mon-Fri at 8AM.
Question is, if it is Monday today, how can I capture the records from Friday.
so that I can exclude weekends.
Because if it is Monday, the total sales displays 0 which actually makes sense because Sunday is a not a work day. Please assist.
See my current code:
SELECT
CONVERT(VARCHAR, DATEADD(dd, - 1, GETDATE()), 103) AS Date,
'Sales Orders' AS Type,
COUNT(o.SalesOrderID) AS Orders,
SUM(d.QtyOrdered) AS Chairs,
ISNULL(ROUND(SUM(d.ExtendedPrice), 2), 0) AS [Total Ex GST]
FROM
dbo.SalesOrder o
LEFT OUTER JOIN
dbo.SalesOrderDetails d ON o.SalesOrderID = d.SalesOrderID
WHERE
(o.EntryDate >= CONVERT(CHAR(8), DATEADD(dd, - 1, GETDATE()), 112))
AND (o.EntryDate < CONVERT(CHAR(8), GETDATE(), 112))
AND (o.CustomerID <> 187);
You can use datediff(dd,0,getdate()) % 7 = 0 to determine if the current date is a Monday regardless of any other server settings (this is because the zero date in SQL Server is 1900-01-01 which happens to be a Monday).
declare #start date;
declare #finish date;
set #start = dateadd(dd, case when datediff(dd,0,getdate()) % 7 = 0 then -3 else -1 end, getdate());
set #finish = dateadd(dd,1,#start);
select
#start, datename(weekday,#start)
, #finish, datename(weekday,#finish)
, datename(weekday,getdate())
;
So in your query I would use:
declare #start date;
declare #finish date;
set #start = dateadd(dd, case when datediff(dd,0,getdate()) % 7 = 0 then -3 else -1 end, getdate());
set #finish = dateadd(dd,1,#start);
SELECT
CONVERT(VARCHAR, #start, 103) AS Date,
'Sales Orders' AS Type,
COUNT(o.SalesOrderID) AS Orders,
SUM(d.QtyOrdered) AS Chairs,
ISNULL(ROUND(SUM(d.ExtendedPrice), 2), 0) AS [Total Ex GST]
FROM
dbo.SalesOrder o
LEFT OUTER JOIN
dbo.SalesOrderDetails d ON o.SalesOrderID = d.SalesOrderID
WHERE
o.EntryDate >= #start
AND o.EntryDate < #finish
AND o.CustomerID <> 187
;
If you use a case statement to determine how many days in the past you need to go e.g.
dateadd(dd, case when datepart(weekday,getdate()) = 1 then -3 else -1 end, getdate()) -- StartDate
dateadd(dd, case when datepart(weekday,getdate()) = 1 then -2 else 0 end, getdate()) -- EndDate
so your code would look like
SELECT
CONVERT(VARCHAR, dateadd(dd, case when datepart(weekday,getdate()) = 1 then -3 else -1 end, getdate()), 103) AS Date,
'Sales Orders' AS Type,
COUNT(o.SalesOrderID) AS Orders,
SUM(d.QtyOrdered) AS Chairs,
ISNULL(ROUND(SUM(d.ExtendedPrice), 2), 0) AS [Total Ex GST]
FROM
dbo.SalesOrder o
LEFT OUTER JOIN
dbo.SalesOrderDetails d ON o.SalesOrderID = d.SalesOrderID
WHERE
(o.EntryDate >= CONVERT(CHAR(8), dateadd(dd, case when datepart(weekday,getdate()) = 1 then -3 else -1 end, getdate()), 112))
AND (o.EntryDate < CONVERT(CHAR(8), dateadd(dd, case when datepart(weekday,getdate()) = 1 then -2 else 0 end, getdate()), 112))
AND (o.CustomerID <> 187);
PS: Do confirm that weekday 1 is Monday on your server.

SQL Server 2014 Select total for each day

Hello I am working on a dataset for a report in SSRS
and I have a query which gives the total requests in the backlog :
SELECT
COUNT(*) as NB
FROM p_rqt WITH (NOLOCK)
INNER JOIN p_cpy WITH (NOLOCK) ON p_cpy.CpyInCde = p_rqt.OrigCpyInCde
WHERE
CpyTypInCde IN (27, 31)
AND p_rqt.RqtNatInCde IN (74, 75, 76)
AND HeadRqtInCde = 0
AND p_rqt.OrigCpyInCde LIKE CASE WHEN #Client = 0 THEN '%' ELSE #Client END
AND ((RcvDte < DATEADD(day, 1, #DateDeb) AND RqtEndDte IS NULL) OR
(RcvDte < DATEADD(day, 1, #DateDeb) AND RqtEndDte > DATEADD(day, 1, #DateDeb)))
and I want to retrieve the total amount left per day.
I tried lot of things like this :
SELECT CONVERT(date,rcvdte,103), count(*) as nb
FROM p_rqt p WITH (NOLOCK)
INNER JOIN p_cpy WITH (NOLOCK) ON p_cpy.CpyInCde = p.OrigCpyInCde
WHERE
CpyTypInCde IN (27, 31)
AND p.RqtNatInCde IN (74, 75, 76)
AND HeadRqtInCde = 0
AND ((RcvDte < DATEADD(day, 1, '20170901') AND RqtEndDte IS NULL) OR (RcvDte < DATEADD(day, 1, '20170901') AND RqtEndDte > DATEADD(day, 1, '20170901')))
group by CONVERT(date,rcvdte,103)
order by CONVERT(date,rcvdte,103)
I tried inner join subqueries, Sum and other stuff
but all I can manage to do is to have the number of records added per day
and I want something like this :
date: NB:
01/01/2017 1950
02/01/2017 1954 (+4 items)
03/01/2017 1945 (-9 items)
Thank you
Use LAG:
WITH cte AS (
SELECT
CONVERT(date, rcvdte, 103) AS date,
COUNT(*) AS nb
FROM p_rqt p WITH (NOLOCK)
INNER JOIN p_cpy WITH (NOLOCK)
ON p_cpy.CpyInCde = p.OrigCpyInCde
WHERE
CpyTypInCde IN (27, 31) AND
p.RqtNatInCde IN (74, 75, 76) AND
HeadRqtInCde = 0 AND
((RcvDte < DATEADD(day, 1, '20170901') AND RqtEndDte IS NULL) OR (RcvDte < DATEADD(day, 1, '20170901') AND RqtEndDte > DATEADD(day, 1, '20170901')))
GROUP BY CONVERT(date, rcvdte, 103)
ORDER BY CONVERT(date, rcvdte, 103)
)
SELECT
t1.date,
(SELECT SUM(t2.nb) FROM cte t2 WHERE t2.date <= t1.date) AS nb,
CASE WHEN t1.nb - LAG(t1.nb, 1, t1.nb) OVER (ORDER BY t1.date) > 0
THEN '(+' + (t1.nb - LAG(t1.nb, 1, t1.nb) OVER (ORDER BY t1.date)) + ' items)'
ELSE '(' + (t1.nb - LAG(t1.nb, 1, t1.nb) OVER (ORDER BY t1.date)) + ' items)'
END AS difference
FROM cte t1
ORDER BY t1.date;
So i found a solution but it is really slow,
i still post the answer anyway
DECLARE #Tb TABLE ( Colonne1 Datetime, Colonne2 INT )
DECLARE #Debut Datetime = '01/09/2017'
WHILE #Debut < '13/09/2017'
BEGIN
DECLARE #Compteur int = (
SELECT
COUNT(1) NB
FROM p_rqt WITH (NOLOCK)
INNER JOIN p_cpy WITH (NOLOCK) ON p_cpy.CpyInCde = p_rqt.OrigCpyInCde
WHERE
CpyTypInCde IN (27, 31)
AND p_rqt.RqtNatInCde IN (74, 75, 76)
AND HeadRqtInCde = 0
AND p_rqt.OrigCpyInCde LIKE '%'
AND (
(RcvDte < #Debut AND RqtEndDte IS NULL)
OR
(RcvDte < #Debut AND RqtEndDte > #Debut)
)
)
INSERT INTO #Tb (Colonne1, Colonne2) VALUES (#Debut, #Compteur)
SET #Debut = DATEADD(day, 1, #Debut)
IF #Debut > '13/09/2017'
BREAK
ELSE
CONTINUE
END
SELECT * FROM #Tb

Calculating monthly sales based on a quarter

I need to calculate the monthly sales for a given quarter.
Here is my code to calculate the previous quarter.
set #quarter = datepart(QQ, getdate()) - 1
if #quarter = 0
begin
set #quarter = 4
set #year = datepart(year, getdate()) -1
end
else set #year = datepart(year, getdate())
Here is my code to calculate the average monthly sales for the entire quarter.
SELECT TOP 5 d.sdealer_name, COUNT(c.icontract_id) / 3 as 'AverageMonthlySales'
FROM dealers d
INNER JOIN contracts c
ON c.sdealer_number = d.sdealer_number
WHERE (d.sdealer_number NOT LIKE '%demo%'
AND d.sdealer_status in ('A', 'R')
AND c.sagent_number = #sagent_number
AND c.sstatus in ('P', 'A', 'C', 'E')
AND c.iproduct_type_id in (4)
AND DATEPART(QQ, c.dtcontract_sale_date) = #quarter
AND DATEPART(year, c.dtcontract_sale_date) = #year)
GROUP BY d.sdealer_name
ORDER BY COUNT(distinct c.icontract_id) desc
How would I calculate the total sales for each month dynamically for the given quarter?
ctrl+h ... just replace quarter datepart with month. is there some reason you haven't tried that?
set #month = datepart(month, getdate()) - 1
if #month = 0
begin
set #month = 12
set #year = datepart(year, getdate()) -1
end
else set #year = datepart(year, getdate())
SELECT TOP 5 d.sdealer_name, COUNT(c.icontract_id) as 'AverageMonthlySales'
FROM dealers d
INNER JOIN contracts c
ON c.sdealer_number = d.sdealer_number
WHERE (d.sdealer_number NOT LIKE '%demo%'
AND d.sdealer_status in ('A', 'R')
AND c.sagent_number = #sagent_number
AND c.sstatus in ('P', 'A', 'C', 'E')
AND c.iproduct_type_id in (4)
AND DATEPART(month, c.dtcontract_sale_date) = #month
AND DATEPART(year, c.dtcontract_sale_date) = #year)
GROUP BY d.sdealer_name
ORDER BY COUNT(distinct c.icontract_id) desc

Applying different time period Groupings to a set of data

The following was a pattern I started to use two years ago and it is repeated over and over in my legacy code.
It effectively groups the same data using different time periods.
Is there a standard way I should be approaching this or is this long winded method as good as I'll get?
Another way of putting this question is how can the following be made more concise?
All 4 queries come out of the same data source and all four go into the same output table can these 4 queries be amalgamated into 1 shorter script?
DECLARE #myDate DATETIME = CONVERT(DATETIME,CONVERT(VARCHAR(11),GETDATE(),106));
DECLARE #myFirstDateLastMth CHAR(8) =CONVERT(CHAR(6),DATEADD(mm,-1,#myDate-1),112) + '01';
DECLARE #myFirstDateCurrentMth CHAR(8) =CONVERT(CHAR(6),DATEADD(mm,0,#myDate-1),112) + '01';
DELETE FROM WH.dbo.tb_myTable
--day on day==========
INSERT INTO WH.dbo.tb_myTable
SELECT
TimePeriod =
CASE
WHEN x.DateKey = CONVERT(VARCHAR(11),#myDate - 1,112) THEN 'Day'
WHEN x.DateKey = CONVERT(VARCHAR(11),#myDate - 2,112) THEN 'Day-1'
END,
Game = x.Name,
Score = SUM(x.Score),
Ticks = SUM(x.Ticks),
ScorePerTick = SUM(x.Score)/SUM(x.Ticks)
FROM #LimitedBetinfo x
WHEREx.DateKey >= CONVERT(VARCHAR(11),#myDate - 2,112)
GROUP BY
CASE
WHEN x.DateKey = CONVERT(VARCHAR(11),#myDate - 1,112) THEN 'Day'
WHEN x.DateKey = CONVERT(VARCHAR(11),#myDate - 2,112) THEN 'Day-1'
END,
x.Name;
--wk on wk==========
INSERT INTO WH.dbo.tb_myTable
SELECT
TimePeriod =
CASE
WHEN x.DateKey >= CONVERT(VARCHAR(11),#myDate - 7,112) THEN 'Week'
WHEN x.DateKey < CONVERT(VARCHAR(11),#myDate - 7,112)
AND x.DateKey >= CONVERT(VARCHAR(11),#myDate - 14,112)
THEN 'Week-1'
END,
Game = x.Name,
Score = SUM(x.Score),
Ticks = SUM(x.Ticks),
ScorePerTick = SUM(x.Score)/SUM(x.Ticks)
FROM #LimitedBetinfo x
WHERE x.DateKey >= CONVERT(VARCHAR(11),#myDate - 14,112)
GROUP BY
CASE
WHEN x.DateKey >= CONVERT(VARCHAR(11),#myDate - 7,112) THEN 'Week'
WHEN x.DateKey < CONVERT(VARCHAR(11),#myDate - 7,112)
AND x.DateKey >= CONVERT(VARCHAR(11),#myDate - 14,112)
THEN 'Week-1'
END,
g.Name;
--mth on mth==========
INSERT INTO WH.dbo.tb_myTable
SELECT
TimePeriod =
CASE
WHEN x.DateKey >= CONVERT(VARCHAR(11),#myDate - 28,112) THEN 'Month'
WHEN x.DateKey < CONVERT(VARCHAR(11),#myDate - 28,112)
AND x.DateKey >= CONVERT(VARCHAR(11),#myDate - 56,112)
THEN 'Month-1'
END,
Game = x.Name,
Score = SUM(x.Score),
Ticks = SUM(x.Ticks),
ScorePerTick = SUM(x.Score)/SUM(x.Ticks)
FROM #LimitedBetinfo x
WHERE x.DateKey >= CONVERT(VARCHAR(11),#myDate - 56,112)
GROUP BY
CASE
WHEN x.DateKey >= CONVERT(VARCHAR(11),#myDate - 28,112) THEN 'Month'
WHEN x.DateKey < CONVERT(VARCHAR(11),#myDate - 28,112)
AND x.DateKey >= CONVERT(VARCHAR(11),#myDate - 56,112)
THEN 'Month-1'
END,
g.Name;
--MTD and PrevCalMonth==========
INSERT INTO WH.dbo.tb_myTable
SELECT
TimePeriod
= CASE
WHEN x.DateKey >= #myFirstDateCurrentMth THEN 'MTD'
WHEN x.DateKey < #myFirstDateCurrentMth
AND x.DateKey >=#myFirstDateLastMth THEN 'PrevCalMonth'
END,
Game = x.Name,
Score = SUM(x.Score),
Ticks = SUM(x.Ticks),
ScorePerTick = SUM(x.Score)/SUM(x.Ticks)
FROM #LimitedBetinfo x
WHERE x.DateKey >= CONVERT(CHAR(6),DATEADD(mm,-1,#myDate-1),112) + '01'
GROUP BY
CASE
WHEN x.DateKey >= #myFirstDateCurrentMth THEN 'MTD'
WHEN x.DateKey < #myFirstDateCurrentMth
AND x.DateKey >=#myFirstDateLastMth THEN 'PrevCalMonth'
END,
g.Name;
I would make it a single insert statement.
Would prefer for now not to use the group by grouping sets, cube, or rollup as that I don't see how I could limit the rows calculated over individual day groups from being less than those calculated over larger time period groups.
So, to keep that from happening you could create a common-table-expression (;WITH mycte AS (...subquery...)), temp table, table variable, or XML formatted text object that would contain the time periods, one row/element for each.
This script can also be run with more or less time periods defined to get all results with only one trip from the app to the server.
Here's an example with temp table, that could also be easily made into a table variable:
--Define time periods
CREATE TABLE #TempTimePeriods (
TimePeriod VARCHAR(20) PRIMARY KEY,
TPBegin VARCHAR(11) NOT NULL,
TPEnd VARCHAR(11) NULL
);
DECLARE #myDate DATETIME = '2012-10-10';
DECLARE #myDateMinusOne DATETIME = DATEADD(dd, -1, #myDate);
INSERT INTO #TempTimePeriods ( TimePeriod, TPBegin, TPEnd )
SELECT [TimePeriod], CONVERT(VARCHAR(11), TPBegin, 112) TPBegin, CONVERT(VARCHAR(11), TPEnd, 112) TPEnd
FROM (
SELECT 'Day' [TimePeriod], #myDate - 1 TPBegin, #myDate - 1 TPEnd UNION ALL
SELECT 'Day-1' [TimePeriod], #myDate - 2 TPBegin, #myDate - 2 TPEnd UNION ALL
SELECT 'Week' [TimePeriod], #myDate - 7 TPBegin, NULL TPEnd UNION ALL
SELECT 'Week-1' [TimePeriod], #myDate - 14 TPBegin, #myDate - 8 TPEnd UNION ALL
SELECT 'Month' [TimePeriod], #myDate - 28 TPBegin, NULL TPEnd UNION ALL
SELECT 'Month-1' [TimePeriod], #myDate - 56 TPBegin, #myDate - 29 TPEnd UNION ALL
SELECT 'MTD' [TimePeriod], DATEADD(dd, -1 * DAY(#myDateMinusOne) + 1, #myDateMinusOne) TPBegin, NULL TPEnd UNION ALL
SELECT 'PrevCalMonth' [TimePeriod], DATEADD(mm,-1,DATEADD(dd, -1 * DAY(#myDateMinusOne) + 1, #myDateMinusOne)) TPBegin, DATEADD(dd, -1 * DAY(#myDateMinusOne), #myDateMinusOne) TPEnd
) TT;
And here is the main query...
--compute/insert results
INSERT INTO WH.dbo.tb_myTable
SELECT TimePeriods.TimePeriod,
x.Name Game,
SUM(x.Score) Score,
SUM(x.Ticks) Ticks,
CASE WHEN SUM(x.Ticks) != 0 THEN SUM(x.Score)/SUM(x.Ticks) END ScorePerTick
FROM #TempTimePeriods TimePeriods
--for periods with no data use left outer join to return 0-value results, otherwise inner join
LEFT OUTER JOIN #LimitedBetInfo x
ON x.DateKey >= [TimePeriods].TPBegin
AND (
[TimePeriods].TPEnd IS NULL
OR x.DateKey <= [TimePeriods].TPEnd
)
GROUP BY TimePeriods.TimePeriod, x.Name
You could also eliminate the the #TempTimePeriods table using a Common-Table-Expression below:
DECLARE #myDate DATETIME = '2012-10-10';
DECLARE #myDateMinusOne DATETIME = DATEADD(dd, -1, #myDate);
;WITH TimePeriods AS (
SELECT [TimePeriod], CONVERT(VARCHAR(11), TPBegin, 112) TPBegin, CONVERT(VARCHAR(11), TPEnd, 112) TPEnd
FROM (
SELECT 'Day' [TimePeriod], #myDate - 1 TPBegin, #myDate - 1 TPEnd UNION ALL
SELECT 'Day-1' [TimePeriod], #myDate - 2 TPBegin, #myDate - 2 TPEnd UNION ALL
SELECT 'Week' [TimePeriod], #myDate - 7 TPBegin, NULL TPEnd UNION ALL
SELECT 'Week-1' [TimePeriod], #myDate - 14 TPBegin, #myDate - 8 TPEnd UNION ALL
SELECT 'Month' [TimePeriod], #myDate - 28 TPBegin, NULL TPEnd UNION ALL
SELECT 'Month-1' [TimePeriod], #myDate - 56 TPBegin, #myDate - 29 TPEnd UNION ALL
SELECT 'MTD' [TimePeriod], DATEADD(dd, -1 * DAY(#myDateMinusOne) + 1, #myDateMinusOne) TPBegin, NULL TPEnd UNION ALL
SELECT 'PrevCalMonth' [TimePeriod], DATEADD(mm,-1,DATEADD(dd, -1 * DAY(#myDateMinusOne) + 1, #myDateMinusOne)) TPBegin, DATEADD(dd, -1 * DAY(#myDateMinusOne), #myDateMinusOne) TPEnd
) TT
)
INSERT INTO WH.dbo.tb_myTable
SELECT TimePeriods.TimePeriod,
x.Name Game,
SUM(x.Score) Score,
SUM(x.Ticks) Ticks,
CASE WHEN SUM(x.Ticks) != 0 THEN SUM(x.Score)/SUM(x.Ticks) END ScorePerTick
FROM [TimePeriods]
--for periods with no data use left outer join to return 0-value results, otherwise inner join
LEFT OUTER JOIN #LimitedBetInfo x
ON x.DateKey >= [TimePeriods].TPBegin
AND (
[TimePeriods].TPEnd IS NULL
OR x.DateKey <= [TimePeriods].TPEnd
)
GROUP BY [TimePeriods].TimePeriod, x.Name
And lastly you could define the time periods in an XML string-handy for passing to a stored procedure if that's your preference and proceed as follows:
--example XML string with time period definitions
DECLARE #TimePeriodsXml NVARCHAR(MAX) = '
<TimePeriod name="Day" tpbegin="20121010" tpend="20121010" />
<TimePeriod name="Day-1" tpbegin="20121009" tpend="20121009" />
<TimePeriod name="Week" tpbegin="20121004"/>
<TimePeriod name="Week-1" tpbegin="20120927" tpend="20121004" />
<TimePeriod name="Month" tpbegin="20120913" />
<TimePeriod name="Month-1" tpbegin="20120815" tpend="20120912" />
<TimePeriod name="MTD" tpbegin="20121001" />
<TimePeriod name="PrevCalMonth" tpbegin="20120901" tpend="20120930" />
';
and the main query modified to read the XML:
SELECT TimePeriods.TimePeriod,
x.Name Game,
SUM(x.Score) Score,
SUM(x.Ticks) Ticks,
CASE WHEN SUM(x.Ticks) != 0 THEN SUM(x.Score)/SUM(x.Ticks) END ScorePerTick
FROM (
SELECT
E.TimePeriod.value('./#name', 'VARCHAR(20)') TimePeriod,
E.TimePeriod.value('./#tpbegin', 'VARCHAR(20)') TPBegin,
E.TimePeriod.value('./#tpend', 'VARCHAR(20)') TPEnd
FROM (
SELECT CAST(#TimePeriodsXml AS XML) tpxml
) TT
CROSS APPLY tpxml.nodes('/TimePeriod') AS E(TimePeriod)
) TimePeriods
--for periods with no data use left outer join to return 0-value results, otherwise inner join
LEFT OUTER JOIN #LimitedBetInfo x
ON x.DateKey >= [TimePeriods].TPBegin
AND (
[TimePeriods].TPEnd IS NULL
OR x.DateKey <= [TimePeriods].TPEnd
)
GROUP BY TimePeriods.TimePeriod, x.Name
For an example of how the XML stringed query could be turned into a procedure, to support a single parameter of 1 or more time periods:
CREATE PROCEDURE dbo.GetTimePeriodAggregates
#TimePeriodsXmlString NVARCHAR(MAX)
AS
BEGIN
SET NOCOUNT ON;
SELECT TimePeriods.TimePeriod,
x.Name Game,
SUM(x.Score) Score,
SUM(x.Ticks) Ticks,
CASE WHEN SUM(x.Ticks) != 0 THEN SUM(x.Score)/SUM(x.Ticks) END ScorePerTick
FROM (
SELECT
E.TimePeriod.value('./#name', 'VARCHAR(20)') TimePeriod,
E.TimePeriod.value('./#tpbegin', 'VARCHAR(20)') TPBegin,
E.TimePeriod.value('./#tpend', 'VARCHAR(20)') TPEnd
FROM (
SELECT CAST(#TimePeriodsXml AS XML) tpxml
) TT
CROSS APPLY tpxml.nodes('/TimePeriod') AS E(TimePeriod)
) TimePeriods
LEFT OUTER JOIN #LimitedBetInfo x
ON x.DateKey BETWEEN TimePeriods.TPBegin AND TimePeriods.TPEnd
GROUP BY TimePeriods.TimePeriod, x.Name
END
Which could be run as:
--This declare is just an example, it could be instead a parameter passed from an application
DECLARE #ThisExecutionsXmlString NVARCHAR(MAX) = N'
<TimePeriod name="Day" tpbegin="20121010" tpend="20121010" />
<TimePeriod name="Day-1" tpbegin="20121009" tpend="20121009" />
<TimePeriod name="Week" tpbegin="20121004"/>
<TimePeriod name="Week-1" tpbegin="20120927" tpend="20121004" />
<TimePeriod name="Month" tpbegin="20120913" />
<TimePeriod name="Month-1" tpbegin="20120815" tpend="20120912" />
<TimePeriod name="MTD" tpbegin="20121001" />
<TimePeriod name="PrevCalMonth" tpbegin="20120901" tpend="20120930" />
';
INSERT INTO WH.dbo.tb_myTable
EXEC dbo.GetTimePeriodAggregates #TimePeriodsXmlString=#ThisExecutionsXmlString
You can create this stored procedure
CREATE PROCEDURE InsertData
#minLimit date,
#maxLimit date,
#minTerm nvarchar(50),
#maxTerm nvarchar(50)
AS
BEGIN
SET NOCOUNT ON;
INSERT INTO tb_myTable
SELECT
[TimePeriod] = CASE WHEN x.DateKey >= #maxLimit THEN #maxTerm ELSE #minTerm END,
[Game] = x.Name,
[Score] = SUM(x.[Score]),
[Ticks] = SUM(x.[Ticks]),
[ScorePerTick] = SUM(x.[Score])/SUM(x.[Ticks])
FROM #LimitedBetinfo x
WHERE x.DateKey >= #minLimit
GROUP BY
CASE WHEN x.DateKey >= #maxLimit THEN #maxTerm ELSE #minTerm END,
x.Name
END
GO
And use like this
TRUNCATE TABLE tb_myTable
DECLARE #today date = cast(getdate() as date)
DECLARE #yesterday date = dateadd(day, -1, #today)
EXECUTE dbo.InsertData #yesterday, #today, N'Day-1', N'Day'
DECLARE #thisweek date = DATEADD(ww, DATEDIFF(ww,0,GETDATE()), 0)
DECLARE #lastweek date = DATEADD(ww, -1, #thisweek)
EXECUTE dbo.InsertData #lastweek, #thisweek, N'Week-1', N'Week'
DECLARE #prev28 date = dateadd(day, -28, #today)
DECLARE #prev56 date = dateadd(day, -56, #today)
EXECUTE dbo.InsertData #prev56, #prev28, N'Month-1', N'Month'
DECLARE #thismonth date = DATEADD(mm, DATEDIFF(mm,0,GETDATE()), 0)
DECLARE #lastmonth date = DATEADD(mm, -1, #thismonth)
EXECUTE dbo.InsertData #lastmonth, #thismonth, N'PrevCalMonth', N'MTD'
Use parameters - VALUES As a Table Source and apply them as parameters in CROSS APPLY with derived table
DECLARE #myDate datetime = CAST(GETDATE() AS date);
IF OBJECT_ID('WH.dbo.tb_myTable') IS NOT NULL DROP TABLE WH.dbo.tb_myTable
SELECT TimePeriod, Game, Score, Ticks, ScorePerTicks
INTO WH.dbo.tb_myTable
FROM (VALUES('Day', DATEADD(day, -1, #myDate), #myDate),
('Day-1', DATEADD(day, -2, #myDate), DATEADD(day, -2, #myDate)),
('Week', DATEADD(day, -7, #myDate), #myDate),
('Week-1', DATEADD(day, -14, #myDate), DATEADD(day, -8, #myDate)),
('Month', DATEADD(day, -28, #myDate), #myDate),
('Month-1', DATEADD(day, -56, #myDate), DATEADD(day, -29, #myDate)),
('MTD', DATEADD(DAY, 1 - DAY(#myDate), #myDate), #myDate),
('PrevCalMonth', DATEADD(DAY, 1 - DAY(#myDate), DATEADD(MONTH, -1, #myDate)), DATEADD(DAY, - DAY(#myDate), #myDate)))
RParameters(TimePeriod, BDate, EDate)
CROSS APPLY (SELECT x.Name AS Game,
SUM(x.Score) AS Score,
SUM(x.Ticks) AS Ticks,
SUM(x.Score) / SUM(x.Ticks) AS ScorePerTicks
FROM #LimitedBetinfo x
WHERE DateKey BETWEEN RParameters.BDate AND RParameters.EDate
GROUP BY Name) AS o
Demo on SQLFiddle
A possible improvement on fred's answer. Not in terms of speed, just readability / modifiability by removing the extra CASE. As a suggestion, I also replaced the passing of both strings (e.g. DAY and DAY-1) with a single string and to have the other just be a concat; this would however cause PrevCalMonth to be displayed as MTD-1 instead (though there are some work-arounds for this).
CREATE PROCEDURE InsertData
#minLimit date, #maxLimit date, #string nvarchar(50)
AS
INSERT INTO tb_myTable
SELECT TimePeriod, Name, SUM(Score) Score, SUM(Ticks) Ticks,
SUM(Score)/SUM(Ticks) ScorePerTick
FROM
(
SELECT *, /* or 'Name, Score, Ticks,' */
TimePeriod = CASE WHEN x.DateKey >= #maxLimit THEN #string ELSE #string+'-1' END
FROM #LimitedBetinfo x
WHERE x.DateKey >= #minLimit
) A
GROUP BY TimePeriod, Name
GO
And use like this:
TRUNCATE TABLE tb_myTable
DECLARE #today date = cast(getdate() as date)
DECLARE #yesterday date = dateadd(day, -1, #today)
EXECUTE dbo.InsertData #yesterday, #today, N'Day'
DECLARE #thisweek date = DATEADD(ww, DATEDIFF(ww,0,GETDATE()), 0)
DECLARE #lastweek date = DATEADD(ww, -1, #thisweek)
EXECUTE dbo.InsertData #lastweek, #thisweek, N'Week'
DECLARE #prev28 date = dateadd(day, -28, #today)
DECLARE #prev56 date = dateadd(day, -56, #today)
EXECUTE dbo.InsertData #prev56, #prev28, N'Month'
DECLARE #thismonth date = DATEADD(mm, DATEDIFF(mm,0,GETDATE()), 0)
DECLARE #lastmonth date = DATEADD(mm, -1, #thismonth)
EXECUTE dbo.InsertData #lastmonth, #thismonth, N'MTD'
It seems that this may be the job for CUBE groupings.
Sorry, I will not give you exact solution to your problem, but the MOCKUP form of select should be like:
select * from
(
select *,count(*) amount from
(
select datepart(HOUR, login_time) as hour,
datepart(MINUTE, login_time) as minute,
cmd as name
from sys.sysprocesses
) tmp
group by cube(tmp.hour, tmp.minute, tmp.name)
) tmp2
where tmp2.name is not null and
(
(tmp2.hour is not null and tmp2.minute is null) or
(tmp2.hour is null and tmp2.minute is not null)
)
One minus - that cube generates too much data for your problem here. So it needs to be filtered out. A big plus would be that you will only need just ONE select into temporary table.

SQL Server + Getting sales query for current and previous month

I have a query below which draws the quantity of sales committed by men for the current as well as previous month, I like to find out if it is efficient to do it this way because it looks repetitive and if I were to get the report for a year then the query will be extremely long. Please advice if I can in any ways improve on this query, I am looking at performance improvements or even code reduction. Thanks.
Declare #CurrentMonth varchar(20)
Declare #PreviousMonth varchar(20)
Set #CurrentMonth =
(
select count(*) from transact t
join card c
on (t.cardno = c.cardno)
join member m
on (c.Memberid = m.id)
where mode ='1'
and voidby is null
and gender='M'
and month(transactdate) = month(getdate())
)
Set #PreviousMonth =
(
select count(*) from transact t
join card c
on (t.cardno = c.cardno)
join member m
on (c.Memberid = m.id)
where mode='1'
and voidby is null
and gender='M'
and month(transactdate) = month(dateadd(month, -1, (getdate())))
)
select #currentMonth, #PreviousMonth
Please check result with your previous version. Quite important it can use index on transactdate if such exists.
declare #CurMonth int
declare #PrevMonth int
select #PrevMonth = sum(
case
when transactdate < select dateadd(mm, datediff(mm, 0, getdate()), 0)
then 1 else 0 end
),
#CurMonth = sum(
case
when transactdate > select dateadd(mm, datediff(mm, 0, getdate()), 0)
then 1 else 0 end
)
from transact t
join card c on t.cardno = c.cardno
join member m on c.Memberid = m.id
where mode ='1'
and voidby is null
and gender='M'
and transactdate >= dateadd(mm, datediff(mm, 0, getdate()) - 1, 0)
and transactdate < dateadd(mm, datediff(mm, 0, getdate()) + 1, 0)