W zeszłym tygodniu jeden z moich kolegów poprosił mnie o pomoc w napisaniu zapytania w celu uzupełnienia brakujących dat w wynikach zapytania. Natknąłem się na kilka rozwiązań, żadne nie wydawało mi się wygodne. Skompilowałem więc własne, używając rekurencyjnego CTE lub Common Table Expression.
Oświadczenie o problemie
Załóżmy, że mamy tabelę, która zawiera zapisy rozmów przychodzących do obsługi klienta od 1 do 10 czerwca 2021 r. W niektóre dni nie ma rejestru rozmów. Jeśli uruchomimy instrukcję GROUP BY w kolumnie datetime, będzie brakować kilku dni. Pożądanym wyjściem jest, że brakujące daty będą miały wartość 0. Przykładowe dane wyjściowe będą poniżej:
Zapytanie
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)
Przykładowe wyjście
Pożądane wyjście
Moje podejście do rozwiązania
Zamiast używać prostego zapytania GROUP BY, używane są CTE i SUB QUERY. Rekursywne CTE jest używane do generowania zakresu dat, a LEFT OUTER JOIN jest używane do łączenia wartości z datą. Wyjaśnijmy krok po kroku.
CTE/wspólne wyrażenie tabelowe
CTE lub Common Table Expression określa tymczasowy nazwany zestaw wyników, który pochodzi z prostego zapytania i jest zdefiniowany w zakresie wykonywania pojedynczej instrukcji SELECT/INSERT/UPDATE/DELETE/MERGE/CREATE VIEW. Może odnosić się również do siebie, co nazywa się rekursywnym CTE.
Przygotowywanie danych
-- 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')
Zbuduj zapytanie
Najpierw napiszemy CTE, które wygeneruje wszystkie daty w zakresie dat.
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;
Teraz to CTE zostanie zrefaktoryzowane, aby utworzyć podzapytanie z LEFT OUTER JOIN, tak aby pojawiła się data, która nie ma tej wartości i zawiera wartość 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
Końcowe wyjście
Wniosek
Mam nadzieję, że będzie to dla ciebie pomocne. Miłego TSQLingu!
Jest również dostępny na moim osobistym blogu!