La semaine dernière, un de mes collègues m'a demandé de l'aider à écrire une requête pour remplir les dates manquantes dans la sortie de la requête. Je suis tombé sur quelques solutions, aucune ne me semblait pratique. J'ai donc compilé le mien en utilisant CTE récursif ou Common Table Expression.
Énoncé du problème
Supposons que nous ayons un tableau contenant les enregistrements d'appels entrants d'un service client du 1er au 10 juin 2021. Certains jours, il n'y a aucun enregistrement d'appel. Si nous exécutons l'instruction GROUP BY sur la colonne datetime, certains jours seront manquants. La sortie souhaitée est que les dates manquantes auront la valeur 0. Un exemple de sortie sera ci-dessous :
Requête
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)
Exemple de sortie
Sortie souhaitée
Mon approche de la solution
Plutôt que d'utiliser une simple requête GROUP BY, CTE et SUB QUERY sont utilisés. CTE récursif est utilisé pour générer la plage de dates et LEFT OUTER JOIN est utilisé pour combiner la valeur avec la date. Expliquons étape par étape.
CTE/Expression de table commune
CTE ou Common Table Expression spécifie un jeu de résultats nommé temporaire dérivé d'une requête simple et défini dans la portée d'exécution d'une seule instruction SELECT/INSERT/UPDATE/DELETE/MERGE/CREATE VIEW. Il peut également se référer à lui-même, ce qui est appelé CTE récursif.
Préparation des données
-- 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')
Créer la requête
Tout d'abord, nous allons écrire un CTE qui générera toutes les dates dans la plage de dates.
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;
Maintenant, ce CTE sera refactorisé pour faire une sous-requête avec LEFT OUTER JOIN afin que la date qui n'a pas la valeur apparaisse et contienne la valeur 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
Résultat final
Conclusion
J'espère que cela vous sera utile. Joyeux TSQL !
Il est également disponible sur mon blog personnel !