Minulý týden mě jeden z kolegů požádal, abych mu pomohl napsat dotaz na doplnění chybějících dat ve výstupu dotazu. Narazil jsem na několik řešení, ani jedno se mi nezdálo vhodné. Takže jsem sestavil svůj vlastní pomocí rekurzivního CTE nebo Common Table Expression.
Prohlášení o problému
Řekněme, že máme tabulku, která obsahuje záznamy o příchozích hovorech zákaznické péče od 1. do 10. června 2021. V některých dnech neexistuje žádný záznam hovorů. Pokud spustíme příkaz GROUP BY ve sloupci datetime, některé dny budou chybět. Požadovaný výstup je, chybějící data budou mít hodnotu 0. Ukázkový výstup bude níže:
Dotaz
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)
Ukázkový výstup
Požadovaný výstup
Můj přístup k řešení
Spíše než pomocí jednoduchého dotazu GROUP BY se používají CTE a SUB QUERY. Rekurzivní CTE se používá ke generování rozsahu dat a LEFT OUTER JOIN se používá ke spojení hodnoty s datem. Pojďme si to vysvětlit krok za krokem.
CTE/Common Table Expression
CTE nebo Common Table Expression určuje dočasnou pojmenovanou sadu výsledků, která je odvozena z jednoduchého dotazu a je definována v rozsahu provádění jediného příkazu SELECT/INSERT/UPDATE/DELETE/MERGE/CREATE VIEW. Může také odkazovat na sebe, což se nazývá rekurzivní CTE.
Příprava dat
-- 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')
Sestavení dotazu
Nejprve napíšeme CTE, která vygeneruje všechna data v daném časovém období.
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;
Nyní bude tento CTE refaktorován tak, aby byl vytvořen dílčí dotaz s LEFT OUTER JOIN, takže se objeví datum, které nemá hodnotu a obsahuje hodnotu 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
Konečný výstup
Závěr
Doufám, že to pro vás bude užitečné. Šťastné TSQLing!
Je také k dispozici na mém osobním blogu!