Combining SQL querys in to one query - sql

Hi i have a set of querys for getting data
two counting rows for the day and two for night, what i would like is to combine these in to one query.
SELECT controllerID
,COUNT(CardID) AS GoodCountDay
FROM ReaderData
WHERE (
ReaderTime BETWEEN '08:00:00'
AND '20:00:00'
)
AND (CardID = 'fffffff0')
AND (DATEDIFF(DAY, DATEADD(DAY, - 0, CURRENT_TIMESTAMP), dtReading) = 0)
GROUP BY controllerID
SELECT controllerID
,COUNT(CardID) AS ScrapCountDay
FROM ReaderData
WHERE (
ReaderTime BETWEEN '08:00:00'
AND '20:00:00'
)
AND (CardID = '007CF00B')
AND (DATEDIFF(DAY, DATEADD(DAY, - 0, CURRENT_TIMESTAMP), dtReading) = 0)
GROUP BY controllerID
SELECT controllerID
,COUNT(CardID) AS GoodCountNight
FROM ReaderData
WHERE (dtReading >= DATEADD(hour, 20, DATEADD(day, - 1, CAST(CAST(GETDATE() AS DATE) AS DATETIME))))
AND (dtReading < DATEADD(hour, 8, CAST(CAST(GETDATE() AS DATE) AS DATETIME)))
GROUP BY controllerID
SELECT controllerID
,count(CardID) AS ScrapCountNight
FROM ReaderData
WHERE dtReading >= dateadd(hour, 20, dateadd(day, - 1, cast(cast(getdate() AS DATE) AS DATETIME)))
AND dtReading < dateadd(hour, 8, cast(cast(getdate() AS DATE) AS DATETIME))
AND (CardID = '007CF00B')
GROUP BY controllerID
All of these querys have the same out put which looks like this
controllerID GoodCountDay
2 207
28 245
30 267
33 314
35 471
37 65
38 17
40 175
Is there any way i can combine these in to one query so the out put will be such
controllerID GoodCountDay ScrapCountDay GoodCountNight ScrapCountNight
2 207 12 123 1
28 245 123
30 267
33 314
35 471
37 65
38 17
40 175

You can use conditional aggregation:
SELECT controllerID,
SUM(CASE WHEN ReaderTime BETWEEN '08:00:00' AND '20:00:00' AND
DATEDIFF(DAY, DATEADD(DAY, - 0, CURRENT_TIMESTAMP), dtReading) = 0) AND
CardID = 'fffffff0' THEN 1 ELSE 0 END) as GoodCountDay
SUM(CASE WHEN ReaderTime BETWEEN '08:00:00' AND '20:00:00' AND
DATEDIFF(DAY, DATEADD(DAY, - 0, CURRENT_TIMESTAMP), dtReading) = 0) AND
CardID = '007CF00B' THEN 1 ELSE 0 END) as ScrapCountDay,
SUM(CASE WHEN dtReading >= DATEADD(hour, 20, DATEADD(day, - 1, CAST(CAST(GETDATE() AS date) AS datetime)))) AND
(dtReading < DATEADD(hour, 8, CAST(CAST(GETDATE() AS date) AS datetime)) AND
CardID = 'fffffff0' THEN 1 ELSE 0 END) as GoodCountNight
SUM(CASE WHEN dtReading >= DATEADD(hour, 20, DATEADD(day, - 1, CAST(CAST(GETDATE() AS date) AS datetime)))) AND
(dtReading < DATEADD(hour, 8, CAST(CAST(GETDATE() AS date) AS datetime)) AND
CardID = '007CF00B' THEN 1 ELSE 0 END) as ScrapCountNight
FROM ReaderData
group by controllerID;

Related

Recursive query?

Hello everybody i'm currently stuck with a query for an ssrs report.
The database is a ticket registration for an helpdesk. So every day techs from the cds team log tickets. If they couldn't resolve the ticket they send it to an other team.
I'm looking for a query who retrace the backlog (non closed tickets) for each month and each team at the end of the month.
Ex :
May 26 17h ticket open by the helpdesk
May 27 8h ticket goes to Techsupport N2
June 3 ticket goes to Tech N3
July 2 ticket closed
I need to retrieve this :
Helpdesk May 0 June 0
N2 May 1 June 0
N3 May 0 June 1
database structure :
Table Appel
Noappel
C_equipe (last affected team)
Table actions
Noappel
Noaction
type_action (affect when goes to an other team Clos when closed)
C_equipe (team who done the action)
i made something based on a query found on the web but it works on weeks not months.
DECLARE #DR DATETIME
DECLARE #SM INTEGER
SET #SM=5
SET #DR=getdate()
SELECT T.* FROM (SELECT A.IDT_APPEL AS NOTICKET,
DEBUT.C_ACTION AS C_DEBUT,
FIN.C_ACTION AS C_FIN,
RS.DF_SEM AS F_TEST,
RS.NUM_SEMAINE AS NUM_SEMAINE,
E.L_EQUIPE AS EQUIPETICKET,
EP.L_EQUIPE AS EQUIPESUP,
A.C_UTIL_INT,
CONCAT(util.N_UTIL,' ',util.PRE_UTIL) AS INTERVENANT,
CASE
WHEN Datepart(ISO_WEEK, FIN.D_CREATION) = RS.NUM_SEMAINE
AND Datediff(DAY, FIN.D_CREATION, RS.DF_SEM) < 7
AND FIN.C_TACTION = 'SYS_EQUIPE'
AND Isnull(UTIL.C_ISIPARC, '0') <> '666' THEN 1
ELSE 0
END AS COND_TR,
CASE
WHEN Datepart(ISO_WEEK, FIN.D_CREATION) = RS.NUM_SEMAINE
AND Datediff(DAY, FIN.D_CREATION, RS.DF_SEM) < 7
AND FIN.C_TACTION = 'SYS_EQUIPE'
AND Isnull(UTIL.C_ISIPARC, '0') = '666' THEN 1
ELSE 0
END AS COND_REJ,
CASE
WHEN FIN.C_TACTION IN('SYS_CLOS_TECH','SYS_CLOS')
AND Datepart(ISO_WEEK, FIN.D_CREATION) = RS.NUM_SEMAINE
AND Datediff(DAY, FIN.D_CREATION, RS.DF_SEM) < 7 THEN 1
ELSE 0
END AS COND_CLOS,
CASE
WHEN Datepart(ISO_WEEK, DEBUT.D_CREATION) = RS.NUM_SEMAINE
AND Datediff(DAY, DEBUT.D_CREATION, RS.DF_SEM) < 7 THEN 1
ELSE 0
END AS NB_TICKET_ENTRE,
CASE
WHEN ( ( FIN.D_CREATION IS NULL
OR FIN.D_CREATION > RS.DF_SEM )
AND DEBUT.D_CREATION < RS.DF_SEM ) THEN ( CASE
WHEN Datediff(DAY, DEBUT.D_CREATION, RS.DF_SEM) - 2 < 5 THEN '05'
WHEN Datediff(DAY, DEBUT.D_CREATION, RS.DF_SEM) - 2 >= 5
AND Datediff(DAY, DEBUT.D_CREATION, RS.DF_SEM) - 2 < 10 THEN '10'
WHEN Datediff(DAY, DEBUT.D_CREATION, RS.DF_SEM) - 2 >= 10
AND Datediff(MONTH, DEBUT.D_CREATION, RS.DF_SEM) < 1 THEN '30'
WHEN Datediff(MONTH, DEBUT.D_CREATION, RS.DF_SEM) >= 1 THEN '30+'
ELSE NULL
END )
ELSE NULL
END AS F_NONCLOS,
CASE
WHEN Datepart(ISO_WEEK, FIN.D_CREATION) = RS.NUM_SEMAINE
AND Datediff(DAY, FIN.D_CREATION, RS.DF_SEM) < 7 THEN ( CASE
WHEN Datediff(DAY, DEBUT.D_CREATION, FIN.D_CREATION) < 1 THEN '01'
WHEN Datediff(DAY, DEBUT.D_CREATION, FIN.D_CREATION) >= 1
AND Datediff(DAY, DEBUT.D_CREATION, FIN.D_CREATION) < 2 THEN '02'
WHEN Datediff(DAY, DEBUT.D_CREATION, FIN.D_CREATION) >= 2
AND Datediff(DAY, DEBUT.D_CREATION, FIN.D_CREATION) < 5 THEN '05'
WHEN Datediff(DAY, DEBUT.D_CREATION, FIN.D_CREATION) >= 5
AND Datediff(DAY, DEBUT.D_CREATION, FIN.D_CREATION) < 15 THEN '15'
WHEN Datediff(DAY, DEBUT.D_CREATION, FIN.D_CREATION) >= 15 THEN '15+'
ELSE NULL
END )
ELSE NULL
END AS F_CLOS,
DEBUT.D_CREATION AS DEBUT,
FIN.D_CREATION AS FIN,
RS.YEAR AS YEARS
FROM APPEL A
INNER JOIN (SELECT B.NO_APPEL,
B.C_ACTION,
B.D_CREATION,
B.C_EQUIPE,
B.C_TACTION,
Lead(B.C_ACTION, 1)
OVER (
PARTITION BY B.NO_APPEL
ORDER BY B.C_ACTION) AS FIN_C_ACTION
FROM ACTIONS B WITH (NOLOCK)
WHERE B.C_TACTION IN ( 'SYS_EQUIPE', 'SYS_CLOS_TECH', 'SYS_REOUVERT','SYS_CLOS' )
AND ( CAST(B.D_CREATION AS DATE)< Dateadd(week, Datediff(week, 0, Dateadd(WEEK, 0, #DR)), 0) )) DEBUT
ON ( A.NO_APPEL = DEBUT.NO_APPEL
AND DEBUT.C_EQUIPE IN('ETUD_MET_BILIZI')
AND DEBUT.C_TACTION IN( 'SYS_EQUIPE', 'SYS_REOUVERT' ) )
LEFT JOIN ACTIONS FIN WITH (NOLOCK)
ON ( FIN.C_ACTION = DEBUT.FIN_C_ACTION )
LEFT JOIN UTILISATEUR UTIL WITH (NOLOCK)
ON ( UTIL.C_UTIL = FIN.C_UTIL )
INNER JOIN EQUIPE E WITH (NOLOCK)
ON DEBUT.C_EQUIPE = E.C_EQUIPE
INNER JOIN EQUIPE EP WITH (NOLOCK)
ON E.C_EQUIPE_PERE = EP.C_EQUIPE
INNER JOIN (SELECT DISTINCT Datepart(ISO_WEEK, P.D_JOUR) AS Num_semaine,
Dateadd(SECOND, 59, Dateadd(MINUTE, 59, Dateadd(HOUR, 23, Dateadd(DAY, -1, Dateadd(week, Datediff(week, 0, P.D_JOUR) + 1, 0))))) AS DF_SEM,
YEAR(P.D_JOUR) AS YEAR
FROM PLANNINGS P WITH (NOLOCK)
INNER JOIN TCALEND TC WITH (NOLOCK)
ON TC.C_TCALEND = P.C_TCALEND
AND TC.F_TCALENDSTD = 1
WHERE ( C_TPERIOD <> 3
AND C_TPERIOD <> 9 )) RS
ON ( ( FIN.D_CREATION > Dateadd(DAY, -7, RS.DF_SEM)
OR FIN.D_CREATION IS NULL )
AND (CAST( RS.DF_SEM AS DATE) >= Dateadd(week, Datediff(week, 0, Dateadd(WEEK, - #SM, #DR)), 0)
AND CAST(RS.DF_SEM AS DATE) <= Dateadd(week, Datediff(week, 0, Dateadd(WEEK, 0,#DR)), 0) ) )
WHERE (A.C_NATURE = 'INC' OR A.C_NATURE = 'DMD')
AND ( A.D_CLOTTECH >= Dateadd(week, Datediff(week, 0, Dateadd(WEEK, - #SM, #DR)), 0)
OR A.D_CLOTTECH IS NULL )
UNION
SELECT DISTINCT NULL IDT_APPEL,
NULL C_DEBUT,
NULL C_FIN,
Dateadd(SECOND, 59, Dateadd(MINUTE, 59, Dateadd(HOUR, 23, Dateadd(DAY, -1, Dateadd(week, Datediff(week, 0, P.D_JOUR) + 1, 0))))) AS DF_SEM,
Datepart(ISO_WEEK, P.D_JOUR) AS Num_semaine,
NULL EQUIPETICKET,
NULL EQUIPESUP,
NULL C_UTIL_INT,
NULL INTERVENANT,
0 COND_TR,
0 COND_REJ,
0 COND_CLOS,
0 NB_TICKET_ENTRE,
NULL F_NONCLOS,
NULL F_CLOS,
NULL,
NULL,
--YEAR(P.D_JOUR) AS YEARS
case when Datepart(ISO_WEEK, P.D_JOUR)=1 then YEAR(getdate()) ELSE YEAR(P.D_JOUR) end AS YEARS
FROM PLANNINGS P WITH (NOLOCK)
INNER JOIN TCALEND TC WITH (NOLOCK)
ON TC.C_TCALEND = P.C_TCALEND
AND TC.F_TCALENDSTD = 1
WHERE ( C_TPERIOD <> 3
AND C_TPERIOD <> 9 )
AND Dateadd(SECOND, 59, Dateadd(MINUTE, 59, Dateadd(HOUR, 23, Dateadd(DAY, -1, Dateadd(week, Datediff(week, 0, P.D_JOUR) + 1, 0))))) >= Dateadd(week, Datediff(week, 0, Dateadd(WEEK, - #SM, #DR)), 0)
AND Dateadd(SECOND, 59, Dateadd(MINUTE, 59, Dateadd(HOUR, 23, Dateadd(DAY, -1, Dateadd(week, Datediff(week, 0, P.D_JOUR) + 1, 0))))) <= Dateadd(week, Datediff(week, 0, Dateadd(WEEK, 0, #DR)), 0)) T ORDER BY T.YEARS ASC
Thanks

Perform multiple SUMs on same column but for multiple date range periods

Given the following table:
id member_id paid_amount trx_date
-------------------------------------------
x 1 100 2019-07-01
x 2 50 2019-07-02
x 1 150 2019-07-05
x 2 70 2019-07-08
x 1 90 2019-08-01
I would like to partition this data and get the sum for each member in 30 day intervals starting at the day of query execution and going backwards. My end result should look like this if I execute the query on the 10th of the month:
member_id paid_amount from
----------------------------------
1 250 2019-06-10 (Sum of paid_amount from 06-10 to 07-10 for member 1)
2 120 2019-06-10 (Sum of paid_amount from 06-10 to 07-10 for member 2)
1 90 2019-07-10 (Sum of paid_amount from 07-10 to 08-10 for member 1)
Basically I would like to sum paid_amount depending on the date where the query ran, in this example 10th Sep 2019, so I want the sum for each member for the following periods
2019-08-10 --> 2019-09-10
2019-07-10 --> 2019-08-10
2019-06-10 --> 2019-07-10
2019-05-10 --> 2019-06-10
etc..
This is what I came up with so far:
SELECT
member_id,
SUM(paid_amount),
p
FROM
(
SELECT
member_id,
paid_amount,
CASE WHEN trx_date BETWEEN DATEADD(MONTH, -1, GETUTCDATE()) AND GETUTCDATE() THEN FORMAT(DATEADD(MONTH, -1, GETUTCDATE()), 'yyyy-MM-dd')
WHEN trx_date BETWEEN DATEADD(MONTH, -2, GETUTCDATE()) AND DATEADD(MONTH, -1, GETUTCDATE()) THEN FORMAT(DATEADD(MONTH, -2, GETUTCDATE()), 'yyyy-MM-dd')
WHEN trx_date BETWEEN DATEADD(MONTH, -3, GETUTCDATE()) AND DATEADD(MONTH, -2, GETUTCDATE()) THEN FORMAT(DATEADD(MONTH, -3, GETUTCDATE()), 'yyyy-MM-dd')
WHEN trx_date BETWEEN DATEADD(MONTH, -4, GETUTCDATE()) AND DATEADD(MONTH, -3, GETUTCDATE()) THEN FORMAT(DATEADD(MONTH, -4, GETUTCDATE()), 'yyyy-MM-dd')
WHEN trx_date BETWEEN DATEADD(MONTH, -5, GETUTCDATE()) AND DATEADD(MONTH, -4, GETUTCDATE()) THEN FORMAT(DATEADD(MONTH, -5, GETUTCDATE()), 'yyyy-MM-dd')
WHEN trx_date BETWEEN DATEADD(MONTH, -6, GETUTCDATE()) AND DATEADD(MONTH, -5, GETUTCDATE()) THEN FORMAT(DATEADD(MONTH, -6, GETUTCDATE()), 'yyyy-MM-dd')
WHEN trx_date BETWEEN DATEADD(MONTH, -7, GETUTCDATE()) AND DATEADD(MONTH, -6, GETUTCDATE()) THEN FORMAT(DATEADD(MONTH, -7, GETUTCDATE()), 'yyyy-MM-dd')
WHEN trx_date BETWEEN DATEADD(MONTH, -8, GETUTCDATE()) AND DATEADD(MONTH, -7, GETUTCDATE()) THEN FORMAT(DATEADD(MONTH, -8, GETUTCDATE()), 'yyyy-MM-dd')
WHEN trx_date BETWEEN DATEADD(MONTH, -9, GETUTCDATE()) AND DATEADD(MONTH, -8, GETUTCDATE()) THEN FORMAT(DATEADD(MONTH, -9, GETUTCDATE()), 'yyyy-MM-dd')
-- and many more WHEN clauses ...
END AS p
FROM
my_table
) AS t
GROUP BY
t.member_id,
t.p
While the above works, there are a few problems with it. First, I need to define so many WHEN clauses because I don't know how far the data goes back to. The second issue is that this query starts slowing down a bit when my table is large.
I am using this query for a view so some options are off limit. Is there a better version of this that is smaller and possibly offer better performance?
You can use a recursive CTE to generate the date periods that cover all the data in your table:
WITH md (min_trx_date) AS (
SELECT MIN(trx_date) AS min_trx_date FROM my_table
),
cte (trx_date_end, trx_date_start) AS (
SELECT convert(date, GETUTCDATE()) AS trx_date_end,
convert(date, DATEADD(MONTH, -1, GETUTCDATE())) AS trx_date_start
UNION ALL
SELECT DATEADD(MONTH, -1, trx_date_end),
DATEADD(MONTH, -1, trx_date_start)
FROM cte
JOIN md ON cte.trx_date_start > md.min_trx_date
)
and then JOIN that CTE to your table to get the payments:
SELECT m.member_id,
SUM(m.paid_amount),
cte.trx_date_start
FROM my_table m
JOIN cte ON m.trx_date BETWEEN cte.trx_date_start AND cte.trx_date_end
GROUP BY member_id, trx_date_start
Demo on dbfiddle
You can try to use cte recursive make calendar table.
use LEAD get next month date, then do join with table.
Query 1:
WITH CTE AS(
SELECT CAST('2019-01-10' AS DATE) fromDt,
CAST('2019-12-10' AS DATE) toDt
UNION ALL
SELECT dateadd(month,1,fromDt),toDt
FROM CTE
WHERE dateadd(month,1,fromDt) < toDt
),CTE2 AS(
SELECT fromDt,LEAD(fromDt) OVER(ORDER BY fromDt) nextMonth
FROM CTE
)
SELECT member_id,
fromDt,
SUM(paid_amount) paid_amount
FROM CTE2 c JOIN T t
on t.trx_date BETWEEN c.fromDt and c.nextMonth
GROUP BY member_id,fromDt
Results:
| member_id | fromDt | paid_amount |
|-----------|------------|-------------|
| 1 | 2019-06-10 | 250 |
| 2 | 2019-06-10 | 120 |
| 1 | 2019-07-10 | 90 |
You can replace your function computing p with something simpler, like datediff(day, trx_date, getutcdate()) / 30. First 30-day interval backwards from now will produce value 0, second - 1, etc.
Select member_id, paid_amount, trx_date as from from TABLE
a join Table b where DateDiff(a.trx_date, b.trx_date) =30
group by member _id

Selecting multiple subqueries

I have subqueries that need to return different results. Each subqueries used different aggregate functions like SUM() AND COUNT(*). What I did is I encapsulate them with SELECT (SELECT subquery, SELECT subquery) not sure if this possible.
Expected result:
TwoYears
123
5
SELECT(
(SELECT
(SELECT SUM(AHT)
FROM
(
SELECT
CASE
WHEN DATEDIFF(year, [AgentProfile_CRT].[dbo].[uvw_AHTMaster].[HireDate], GETDATE()) >= 2 AND (DATEDIFF(year, [AgentProfile_CRT].[dbo].[uvw_AHTMaster].[HireDate], GETDATE())) <= 2 OR (DATEDIFF(year, [AgentProfile_CRT].[dbo].[uvw_AHTMaster].[HireDate], GETDATE())) <= 1
THEN [AgentProfile_CRT].[dbo].[uvw_AHTMaster].[Value]
END AS AHT
FROM [AgentProfile_CRT].[dbo].[uvw_AHTMaster] WHERE [AgentProfile_CRT].[dbo].[uvw_AHTMaster].[Month] = 'January' AND [AgentProfile_CRT].[dbo].[uvw_AHTMaster].[Year] = 2018 AND [AgentProfile_CRT].[dbo].[uvw_AHTMaster].[AccountID] = 8 AND [AgentProfile_CRT].[dbo].[uvw_AHTMaster].[LOBID] = 23
) f
WHERE AHT = AHT ) AS TwoYears),
(SELECT
(SELECT COUNT(*) AS TwoYears
FROM
(
SELECT
CASE
WHEN DATEDIFF(year, [AgentProfile_CRT].[dbo].[uvw_AHTMaster].[HireDate], GETDATE()) >= 2 AND (DATEDIFF(year, [AgentProfile_CRT].[dbo].[uvw_AHTMaster].[HireDate], GETDATE())) <= 2 OR (DATEDIFF(year, [AgentProfile_CRT].[dbo].[uvw_AHTMaster].[HireDate], GETDATE())) <= 1
THEN 'Good'
END AS Result
FROM [AgentProfile_CRT].[dbo].[uvw_AHTMaster] WHERE [AgentProfile_CRT].[dbo].[uvw_AHTMaster].[Month] = 'January' AND [AgentProfile_CRT].[dbo].[uvw_AHTMaster].[Year] = 2018 AND [AgentProfile_CRT].[dbo].[uvw_AHTMaster].[AccountID] = 8 AND [AgentProfile_CRT].[dbo].[uvw_AHTMaster].[LOBID] = 23
) f
WHERE Result = 'Good') AS TwoYears)
) a
You need to make a union all and remove some of your subselects.
Like this:
SELECT SUM(AHT) as TwoYears
FROM (
SELECT CASE
WHEN DATEDIFF(year, [AgentProfile_CRT].[dbo].[uvw_AHTMaster].[HireDate], GETDATE()) >= 2
AND (DATEDIFF(year, [AgentProfile_CRT].[dbo].[uvw_AHTMaster].[HireDate], GETDATE())) <= 2
OR (DATEDIFF(year, [AgentProfile_CRT].[dbo].[uvw_AHTMaster].[HireDate], GETDATE())) <= 1
THEN [AgentProfile_CRT].[dbo].[uvw_AHTMaster].[Value]
END AS AHT
FROM [AgentProfile_CRT].[dbo].[uvw_AHTMaster]
WHERE [AgentProfile_CRT].[dbo].[uvw_AHTMaster].[Month] = 'January'
AND [AgentProfile_CRT].[dbo].[uvw_AHTMaster].[Year] = 2018
AND [AgentProfile_CRT].[dbo].[uvw_AHTMaster].[AccountID] = 8
AND [AgentProfile_CRT].[dbo].[uvw_AHTMaster].[LOBID] = 23
) f
WHERE AHT = AHT
union all
SELECT COUNT(*) AS TwoYears
FROM (
SELECT CASE
WHEN DATEDIFF(year, [AgentProfile_CRT].[dbo].[uvw_AHTMaster].[HireDate], GETDATE()) >= 2
AND (DATEDIFF(year, [AgentProfile_CRT].[dbo].[uvw_AHTMaster].[HireDate], GETDATE())) <= 2
OR (DATEDIFF(year, [AgentProfile_CRT].[dbo].[uvw_AHTMaster].[HireDate], GETDATE())) <= 1
THEN 'Good'
END AS Result
FROM [AgentProfile_CRT].[dbo].[uvw_AHTMaster]
WHERE [AgentProfile_CRT].[dbo].[uvw_AHTMaster].[Month] = 'January'
AND [AgentProfile_CRT].[dbo].[uvw_AHTMaster].[Year] = 2018
AND [AgentProfile_CRT].[dbo].[uvw_AHTMaster].[AccountID] = 8
AND [AgentProfile_CRT].[dbo].[uvw_AHTMaster].[LOBID] = 23
) f
WHERE Result = 'Good'

Counting the number of returning item for a timeframe

Context:
For every item returned, we need to know how many time "this" item was return in different timeframe: 30,60,90,120,180,365 days.
An item is unique based on his Serial (Itm_Item_Serial).
Sample Data:
Complete sample with creation script, and expected result here*.
CREATE TABLE ItemReturn
(
[Itm_Id] int,
[Itm_Item_Serial] int,
[Itm_CDate] datetime
);
INSERT INTO ItemReturn ([Itm_Id], [Itm_Item_Serial], [Itm_CDate])
VALUES
(1, 1, '2016-10-02 02:00:00'),
(2, 1, '2016-09-03 02:00:00'),
(3, 1, '2016-11-03 01:00:00')
;
Expected result: for Itm_Item_Serial = 1
Itm_Id 30d 60d 90d 120d 180d 365d
1 0 0 0 0 0 0
2 1 0 0 0 0 0
3 1 1 0 0 0 0
0 or null if there is no return in this time frame.
How does it work: for Itm_Item_Serial = 1
[Itm_Id] [Itm_Item_Serial] [Itm_CDate]
1, 1, '2016-10-02 02:00:00'
2, 1, '2016-09-03 02:00:00'
3, 1, '2016-11-03 01:00:00'
For [Itm_Id]=1, there is 0 previous return.
For [Itm_Id]=2, there is 1 previous return
on '2016-10-02'. datediff = 29. So there is one return in the timeframe "0-30 Days".
For [Itm_Id]=3, there is 2 previous return.
on '2016-09-03'. datediff = 60. So there is one return in the timeframe "30-60 Days".
on '2016-10-02'. datediff = 31. So there is one return in the timeframe "30-60 Days".
*: rextester Data Sample, and ordered Data Sample.
SQL Fiddle: http://sqlfiddle.com/#!6/00460/4
Does not give you the result you provided but does provide the counts for the various day lapses
; with data_CTE as (
select
m1.Itm_CDate
, DATEDIFF(day, m1.Itm_CDate, m2.Itm_CDate) DayDiff
, m1.Itm_id
, m1.Itm_Item_Serial
from ItemReturn m1
left outer join ItemReturn m2
on m1.Itm_Item_Serial = m2.Itm_Item_Serial
where m1.Itm_CDate < m2.Itm_CDate
and m1.Itm_CDate > DATEADD(day, -30, m2.Itm_CDate)
), aggr_CTE as (
select
Itm_Id, Itm_Item_Serial
, case
when DayDiff < 30 then '30d'
when DayDiff < 60 then '60d'
when DayDiff < 90 then '90d'
when DayDiff < 120 then '120d'
when DayDiff < 180 then '180d'
else '365d'
end DayLapse
from data_CTE
)select
Itm_id, [30d], [60d], [90d], [120d], [180d], [365d]
from (
select Itm_id, Itm_Item_Serial, DayLapse
from aggr_CTE
) src PIVOT (
count(Itm_Item_Serial)
for DayLapse in ([30d], [60d], [90d], [120d], [180d], [365d])
) as PivotTable
data_CTE Result (Output):
Itm_CDate DayDiff Itm_id Itm_Item_Serial
----------------------- ----------- ----------- ---------------
2016-09-03 02:00:00.000 29 2 1
2016-09-03 02:00:00.000 29 3 1
2016-09-03 02:00:00.000 29 5 2
2016-09-03 02:00:00.000 29 13 6
2016-08-05 02:00:00.000 29 15 6
2016-08-04 02:00:00.000 1 14 6
Update: 2017-07-26
Final Version of the Query
; with data_CTE as (
SELECT
Itm_Id
, Itm_Item_Serial
, ROW_NUMBER() over (Partition By Itm_Item_Serial order by Itm_Item_Serial) Itm_Item_RowNum
, Itm_CDate
FROM [ItemReturn] ir1
), date_CTE as (
select
d2.Itm_Id
, d1.Itm_Item_Serial
, DATEDIFF(day, d1.Itm_CDate, d2.Itm_CDate) DayDiff
from data_CTE d1, data_CTE d2
where d1.Itm_Item_Serial = d2.Itm_Item_Serial
and DATEDIFF(day, d1.Itm_CDate, d2.Itm_CDate) >= 0
), aggr_CTE as (
select
Itm_Id, Itm_Item_Serial
, case
when DayDiff <= 0 then '0d'
when DayDiff <= 30 then '30d'
when DayDiff <= 60 then '60d'
when DayDiff <= 90 then '90d'
when DayDiff <= 120 then '120d'
when DayDiff <= 180 then '180d'
else '365d'
end DayLapse
from date_CTE
)select
Itm_id, [30d], [60d], [90d], [120d], [180d], [365d]
from (
select Itm_id, Itm_Item_Serial, DayLapse
from aggr_CTE
) src PIVOT (
count(Itm_Item_Serial)
for DayLapse in ([0d], [30d], [60d], [90d], [120d], [180d], [365d])
) as PivotTable
You can query as below:
Select * from (
Select Itm_id, Itm_Item_Serial, case when datediff(day, Itm_CDate, getdate()) between 0 and 30 then '30'
when datediff(day, Itm_CDate, getdate()) between 30 and 60 then '60'
when datediff(day, Itm_CDate, getdate()) between 60 and 90 then '90'
when datediff(day, Itm_CDate, getdate()) between 90 and 120 then '120'
when datediff(day, Itm_CDate, getdate()) between 120 and 180 then '180'
when datediff(day, Itm_CDate, getdate()) between 180 and 360 then '365'
Else 0 End as test
from ItemReturn
) a
pivot (count(test) for test in ([30],[60],[90],[120],[180],[365])) p
If your difference date is between next date then you might need to use lead as below:
;With cte as (
Select *, NextDate = lead(itm_cDate) over(partition by Itm_Item_Serial order by Itm_id)
from ItemReturn
), cte2 as (
Select Itm_id, Itm_Item_Serial, case when datediff(day, Itm_CDate, NextDate) between 0 and 30 then '30'
when datediff(day, Itm_CDate, NextDate) between 30 and 60 then '60'
when datediff(day, Itm_CDate, NextDate) between 60 and 90 then '90'
when datediff(day, Itm_CDate, NextDate) between 90 and 120 then '120'
when datediff(day, Itm_CDate, NextDate) between 120 and 180 then '180'
when datediff(day, Itm_CDate, NextDate) between 180 and 360 then '365'
Else 0 End as test
from cte
)
Select * from cte2
pivot (count(test) for test in ([30],[60],[90],[120],[180],[365])) p
Demo here
not the final answer but slick way to get the difference
with cte as
select [Itm_Id], [Itm_Item_Serial], [Itm_CDate]
, row_number() over (partition by [Itm_Item_Serial] order by [Itm_CDate]) as rn
from ItemReturn;
select t1.*
, datediff(dd, t1.[Itm_CDate], t2.[Itm_CDate] desc) as diff
, t2.[Itm_Id], t2.[Itm_CDate]
from cte t1
join cte t2
on t1.[Itm_Item_Serial] = t2.[Itm_Item_Serial]
and t1.rn = 1
and t2.rn <> 1
order by t1.[Itm_Item_Serial], t1.rn

Ordering dates for 4 weeks

I am writing a SQL query that pulls some information for the last 4 weeks.
I am seeing two issues with my results (see below).
First problem is that when I look back four weeks, the range should be August 10 - September 6. When I order by 'Day of the Month', the dates in September get moved to the top of the results when they should actually be at the end. So my results should start from 10 (august) and end at 6 (September).
Second problem is I'm missing a few random dates (3, 4, 13, 27).
Day of the Month Number of X
1 125
2 77
5 5
6 23
10 145
11 177
12 116
14 2
15 199
16 154
17 134
18 140
19 154
21 8
22 166
23 145
24 151
25 107
26 79
28 3
29 151
30 163
31 147
Here a general version of my query:
DECLARE #startDate datetime, #endDate datetime;
SET #startDate = dateadd(day, -28, GETDATE());
SET #endDate = dateadd(day, -1, GETDATE());
Select DATEPArt(dd, Time) AS 'Day of the Month', count(*) AS ' Number of X'
from SomeTable ST
where Time >= #startDate
AND Time < #endDate
group by DATEPArt(dd, Time)
order by 'Day of the Month'
For the first problem you can order by date to get the correct date order. I use convert to get a time-free date so that the entries group correctly.
DECLARE #StartDate datetime, #EndDate datetime;
SET #StartDate = DATEADD(day, -28, GETDATE());
SET #EndDate = DATEADD(day, -1, GETDATE());
SELECT
DATEPART(dd, Convert(date, Time)) AS 'Day of the Month', COUNT(*) AS ' Number of X'
FROM SomeTable ST
WHERE Time >= #StartDate
AND Time < #EndDate
GROUP BY Convert(date, Time)
ORDER BY Convert(date, Time)
As for the missing days, this is more complicated as the data needs to be there for the group-by to work.
One option is to create a temporary table with all the dates in, then join in the data. This will still leave a row where the join does not find any data, and can get a "zero" count.
DECLARE #StartDate datetime, #EndDate datetime;
SET #StartDate = DATEADD(day, -28, GETDATE());
SET #EndDate = DATEADD(day, -1, GETDATE());
--Create temporary table with all days between the two dates
;WITH d(d) AS
(
SELECT DATEADD(DAY, n, DATEADD(DAY, DATEDIFF(DAY, 0, #StartDate), 0))
FROM ( SELECT TOP (DATEDIFF(DAY, #StartDate, #EndDate) + 1)
n = ROW_NUMBER() OVER (ORDER BY [object_id]) - 1
FROM sys.all_objects ORDER BY [object_id] ) AS n
)
--Join in our query to the table temporary table
SELECT
DATEPART(dd, d.d) AS 'Day of the Month',
COUNT(Time) AS ' Number of X'
FROM d LEFT OUTER JOIN SomeTable ST ON DATEPART(dd, d.d) = DATEPART(dd, Time)
AND Time >= #StartDate
AND Time < #EndDate
GROUP BY d.d
ORDER BY d.d