Förra veckan bad en av mina kollegor mig att hjälpa honom att skriva en fråga för att fylla i saknade datum i frågeutdata. Jag stötte på ett par lösningar, ingen av dem verkade bekväma för mig. Så jag kompilerade min egen med hjälp av rekursiv CTE eller Common Table Expression.
Problembeskrivning
Låt oss säga att vi har en tabell som innehåller inkommande samtalsuppgifter för en kundtjänst från 1:a till 10:e juni 2021. Vissa dagar finns det ingen samtalspost. Om vi kör GROUP BY-satsen på kolumnen datetime, kommer några dagar att saknas. Önskad utdata är, saknade datum kommer att vara 0 värde. Exempelutdata kommer att vara nedan:
Fråga
SELECT CONVERT(varchar(10),B.call_time,111) AS OriginalDate, COUNT(*) as total
FROM Test1 B
GROUP BY CONVERT(varchar(10),B.call_time,111)
ORDER BY CONVERT(varchar(10),B.call_time,111)
Exempelutdata
Önskad utdata
Mitt tillvägagångssätt till lösning
Istället för att använda enkel GROUP BY-fråga används CTE och SUB QUERY. Rekursiv CTE används för att generera datumintervallet och LEFT OUTER JOIN används för att kombinera värdet med datumet. Låt oss förklara steg för steg.
CTE/Common Table Expression
CTE eller Common Table Expression specificerar en temporär namngiven resultatuppsättning som härleds från en enkel fråga och definieras inom exekveringsomfånget för en enda SELECT/INSERT/UPDATE/DELETE/MERGE/CREATE VIEW-sats. Det kan också referera till sig själv, vilket kallas rekursiv CTE.
Förbereder data
-- Create the table
CREATE TABLE Test1(
call_time datetime,
name varchar(10) default ('Mehedi')
)
GO
-- Populate with sample data
INSERT INTO Test1 (call_time, name)
VALUES ('2021-06-01 08:00','A')
,('2021-06-01 09:05','C')
,('2021-06-01 12:50','E')
,('2021-06-01 16:17','D')
,('2021-06-01 18:53','G')
,('2021-06-03 11:07','F')
,('2021-06-03 13:09','A')
,('2021-06-03 16:26','E')
,('2021-06-03 19:56','C')
,('2021-06-03 21:24','A')
,('2021-06-04 19:13','A')
,('2021-06-04 11:45','B')
,('2021-06-04 15:02','C')
,('2021-06-08 23:02','A')
,('2021-06-09 03:04','E')
Skapa frågan
Först kommer vi att skriva en CTE som genererar alla datum inom datumintervallet.
DECLARE @StartDate DATE, @EndDate DATE
SET @StartDate = '2021-11-01'
SET @EndDate = '2021-11-08'
;WITH cte AS
( SELECT @StartDate AS sDate
UNION ALL
SELECT DATEADD(DAY,1,sDate)
FROM cte
WHERE sDate < @EndDate
)
SELECT sDate
FROM cte;
Nu kommer denna CTE att refaktoreras för att göra en underfråga med LEFT OUTER JOIN så att datumet som inte har värdet visas och innehåller värdet 0.
DECLARE @startdate DATETIME = '2021-06-01'
DECLARE @endDate DATETIME = '2021-06-10'
;WITH cte
AS
(
SELECT @startdate as sDate
UNION All
SELECT DATEADD(day,1,sDate) From cte where DATEADD(day,1,sDate) <= @endDate
)
SELECT
C.OriginalDate
,C.total
FROM
(
SELECT CONVERT(varchar(10),A.sDate,111) AS OriginalDate, COUNT(B.call_time) as total
FROM cte A
LEFT OUTER JOIN Test1 B
ON A.sDate = CONVERT(varchar(10),B.call_time,111)
GROUP by CONVERT(varchar(10),A.sDate,111)
) C
ORDER BY C.OriginalDate
Slutlig utdata
Slutsats
Hoppas, det kommer att vara till hjälp för dig. Glad TSQLing!
Den finns också i min personliga blogg!