I sidste uge bad en af mine kolleger mig om at hjælpe ham med at skrive en forespørgsel for at udfylde manglende datoer i forespørgselsoutput. Jeg stødte på et par løsninger, ingen af dem virkede praktiske for mig. Så jeg kompilerede min egen ved hjælp af rekursiv CTE eller Common Table Expression.
Problemerklæring
Lad os sige, at vi har en tabel, der indeholder registreringer af indgående opkald fra en kundeservice fra 1. til 10. juni 2021. Nogle dage er der ingen opkaldspost. Hvis vi kører GROUP BY-sætningen på datetime-kolonnen, vil nogle dage mangle. Ønsket output er, manglende datoer vil være 0 værdi. Eksempeloutput vil være nedenfor:
Forespørgsel
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)
Eksempeloutput
Ønsket output
Min tilgang til løsning
I stedet for at bruge simpel GROUP BY-forespørgsel, bruges CTE og SUB QUERY. Rekursiv CTE bruges til at generere datointervallet, og LEFT OUTER JOIN bruges til at kombinere værdien med datoen. Lad os forklare trin-for-trin.
CTE/fælles tabeludtryk
CTE eller Common Table Expression angiver et midlertidigt navngivet resultatsæt, som er afledt af en simpel forespørgsel og defineret inden for eksekveringsomfanget af en enkelt SELECT/INSERT/UPDATE/DELETE/MERGE/CREATE VIEW-sætning. Det kan også referere til sig selv, hvilket kaldes rekursiv CTE.
Forberedelse af 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')
Byg forespørgslen
Først vil vi skrive en CTE, som genererer alle datoer inden for datointervallet.
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 vil denne CTE blive refaktoreret for at lave en underforespørgsel med LEFT OUTER JOIN, så den dato, der ikke har værdien, vises og indeholder 0-værdien.
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
Endelig output
Konklusion
Håber, det vil være nyttigt for dig. Glad TSQLing!
Den er også tilgængelig på min personlige blog!