Миналата седмица един от моите колеги ме помоли да му помогна да напише заявка, за да попълни липсващите дати в изхода на заявката. Попаднах на няколко решения, нито едното не ми се стори удобно. И така, компилирах моя собствена, използвайки рекурсивно CTE или Common Table Expression.
Постановка на проблема
Да кажем, че имаме таблица, която съдържа записи на входящи обаждания за обслужване на клиенти от 1 до 10 юни 2021 г. В някои дни няма запис на обажданията. Ако изпълним оператора GROUP BY в колона datetime, някои дни ще липсват. Желаният изход е, липсващите дати ще бъдат стойност 0. Примерен изход ще бъде по-долу:
Запитване
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)
Примерен изход
Желан изход
Моят подход към решение
Вместо да се използва проста заявка GROUP BY, се използват CTE и SUB QUERY. Рекурсивният CTE се използва за генериране на диапазона от време, а LEFT OUTER JOIN се използва за комбиниране на стойността с датата. Нека обясним стъпка по стъпка.
CTE/Общ израз на таблица
CTE или Common Table Expression определя временен набор от именуван резултат, който е извлечен от проста заявка и дефиниран в рамките на обхвата на изпълнение на един израз SELECT/INSERT/UPDATE/DELETE/MERGE/CREATE VIEW. Може да се отнася и за себе си, което се нарича рекурсивен CTE.
Подготовка на данни
-- 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')
Създайте заявката
Първо, ще напишем CTE, който ще генерира всички дати в рамките на диапазона от време.
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;
Сега този CTE ще бъде рефакториран, за да се направи подзаявка с LEFT OUTER JOIN, така че датата, която няма стойността, да се появи и да съдържа стойност 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
Краен изход
Заключение
Надявам се, че ще ви бъде полезно. Честит TSQLing!
Предлага се и в моя личен блог!