Vorige week vroeg een van mijn collega's me om hem te helpen een query te schrijven om ontbrekende datums in de query-uitvoer in te vullen. Ik kwam een paar oplossingen tegen, geen van beide leek me handig. Dus heb ik mijn eigen gecompileerd met recursieve CTE of Common Table Expression.
Probleemstelling
Laten we zeggen dat we een tabel hebben met inkomende oproeprecords van een klantenservice van 1 tot 10 juni 2021. Op sommige dagen is er geen oproeprecord. Als we de GROUP BY-instructie in de datetime-kolom uitvoeren, zullen enkele dagen ontbreken. Gewenste uitvoer is, ontbrekende datums hebben een waarde van 0. Voorbeelduitvoer vindt u hieronder:
Zoekopdracht
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)
Voorbeelduitvoer
Gewenste output
Mijn benadering van oplossing
In plaats van een eenvoudige GROUP BY-query te gebruiken, worden CTE en SUB QUERY gebruikt. Recursieve CTE wordt gebruikt om het datumbereik te genereren en LEFT OUTER JOIN wordt gebruikt om de waarde met de datum te combineren. Laten we het stap voor stap uitleggen.
CTE/Common Table Expression
CTE of Common Table Expression specificeert een tijdelijke benoemde resultatenset die is afgeleid van een eenvoudige query en is gedefinieerd binnen het uitvoeringsbereik van een enkele SELECT/INSERT/UPDATE/DELETE/MERGE/CREATE VIEW-instructie. Het kan ook naar zichzelf verwijzen, wat recursieve CTE wordt genoemd.
Gegevens voorbereiden
-- 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')
Bouw de zoekopdracht
Eerst zullen we een CTE schrijven die alle datums binnen het datumbereik zal genereren.
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 wordt deze CTE aangepast om een subquery te maken met LEFT OUTER JOIN, zodat de datum die de waarde niet heeft verschijnt en 0 waarde bevat.
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
Definitieve output
Conclusie
Hoop, het zal nuttig voor je zijn. Veel plezier met TSQLing!
Het is ook beschikbaar in mijn persoonlijke blog!