Pourquoi ne pas créer une table de dates pour se faciliter les recherches dans des tables suivant la colonne de type date qu’elles contiennent ?
On peut vous demander par exemple de rechercher la quantité d’eau consommée par un parc de machines pour la deuxième semaine de chaque mois d’une année.
Imaginez un peu la complexité de la requête à écrire …
Voyons comment on peut résoudre cette demande très facilement en créant une table de dates …
Voici donc notre table :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | --------------------------------- -- Nicolas SOUQUET - 10/07/2009 - --------------------------------- CREATE TABLE TbDates ( date DATETIME NOT NULL CONSTRAINT PK_TbDates_date PRIMARY KEY, annee AS (YEAR(date)) PERSISTED NOT NULL, mois AS(MONTH(date)) PERSISTED NOT NULL, semaineDansAnnee AS(DATEPART(week, date)) PERSISTED NOT NULL, semaineDansMois AS(DATEPART(week, date) - DATEPART(week, DATEADD(day, -DATEPART(day, date) + 1, date)) + 1) PERSISTED NOT NULL, jourDansAnnee AS (DATEPART(dayofyear, date)) PERSISTED NOT NULL, jourDansMois AS(DAY(date)) PERSISTED NOT NULL, jourDansSemaine TINYINT NULL CONSTRAINT CHK_TbDates_jourDansSemaine CHECK(jourDansSemaine BETWEEN 0 AND 7), ) GO |
Comme toutes les colonnes sont calculées à l’exception de jourDansSemaine (parce que la fonction DATEPART(weekday, date) n’est pas déterministe, elle ne peut pas être utilisée comme spécification d’une colonne calculée), nous n’aurons donc qu’à insérer les dates dans la colonne date.
Comment allons-nous valuer la colonne jourDansSemaine ? Avec le trigger suivant :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | --------------------------------------------------------------------------------- -- Nicolas SOUQUET - 10/07/2009 - Déclencheur qui calcule le jour de la semaine - -- pour la table dbo.TbDates - --------------------------------------------------------------------------------- CREATE TRIGGER TR_A_I_TbDates ON dbo.TbDates AFTER INSERT AS BEGIN SET NOCOUNT ON UPDATE dbo.TbDates SET jourDansSemaine = DATEPART(weekday, D.date) FROM dbo.TbDates AS D JOIN INSERTED AS I ON D.date = I.date END GO |
Et pour stocker les dates, il suffira d’exécuter la procédure stockée suivante :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | ----------------------------------------------------------------------------------------- -- Nicolas SOUQUET - 10/07/2009 - Procédure de génération de tous les jours d'une année - ----------------------------------------------------------------------------------------- ALTER PROCEDURE Ps_GenereDatesAnnee @annee SMALLINT AS BEGIN SET NOCOUNT ON DECLARE @dateDeb DATETIME, @dateFin DATETIME SELECT @dateDeb = CAST(CAST(@annee AS VARCHAR) + '0101' AS DATETIME), @dateFin = CAST(CAST(@annee AS VARCHAR) + '1231' AS DATETIME) ;WITH CTE_DATES AS ( SELECT @dateDeb AS Date UNION ALL SELECT Date + 1 FROM CTE_DATES WHERE Date <= @dateFin ) SELECT Date FROM CTE_DATES WHERE Date <= @dateFin OPTION (MAXRECURSION 366) END |
Ainsi pour stocker tous les jours de l’année 2009, il suffira d’exécuter :
1 2 3 4 5 6 7 8 9 10 11 12 | --------------------------------- -- Nicolas SOUQUET - 10/07/2009 - --------------------------------- ALTER TABLE dbo.TbDates ALTER COLUMN jourDansSemaine TINYINT NULL GO INSERT INTO dbo.TbDates (date) EXEC dbo.Ps_GenereDatesAnnee 2009 GO ALTER TABLE dbo.TbDates ALTER COLUMN jourDansSemaine TINYINT NOT NULL GO |
Pour voir ce que contient la table après cette insertion :
1 2 3 4 5 | --------------------------------- -- Nicolas SOUQUET - 10/07/2009 - --------------------------------- SELECT date, annee, mois, semaineDansAnnee, semaineDansMois, jourDansAnnee, jourDansMois, jourDansSemaine FROM dbo.TbDates |
Finalement, pour rechercher la quantité d’eau consommée par un parc de machines pour la deuxième semaine de chaque mois de l’année 2009, nous devrons écrire :
1 2 3 4 5 6 7 8 9 | --------------------------------- -- Nicolas SOUQUET - 10/07/2009 - --------------------------------- SELECT SUM(T.conso) AS consoDeuxiemeSemaine2009 FROM dbo.maTable AS T JOIN dbo.TbDates AS D ON T.colonneDate = D.date WHERE D.semaineDansLeMois = 2 AND D.annee = 2009 |
Une autre exemple : rechercher la consommation moyenne d’eau par semaine dans le mois, pour toute l’année 2009 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | --------------------------------- -- Nicolas SOUQUET - 10/07/2009 - --------------------------------- SELECT semaineDansLeMois, AVG(consoSemaine) FROM ( SELECT D.semaineDansLeMois, SUM(T.conso) AS consoSemaine FROM dbo.maTable AS T JOIN dbo.TbDates AS D ON T.colonneDate = D.date WHERE D.annee = 2009 GROUP BY D.semaineDansLeMois ) AS SUB (semaineDansLeMois, consoSemaine) GROUP BY semaineDansLeMois |
ElSuket
Bonjour iberserk,
Je pense que cela n’aucun impact; c’est plus une habitude de codage qui permet de trouver rapidement les colonnes dans une longue requête.
En revanche il est possible que cela ait un impact si les colonnes sont aliasées (elles n’auraient pas le même nom dans la table dérivée que dans la requête qui la définit).
Je teste et je vous tiens au courant
Je ne connaissais pas la syntaxe de la dernière requete:
« AS SUB(semaineDansLeMois,consoSemaine) »
Quel impact de repréciser les deux colonnes déjà présentes dans le SELECT?