web-dev-qa-db-fra.com

Comparaisons de dates SQL Server basées sur le mois et l'année uniquement

J'ai du mal à déterminer la meilleure façon de comparer les dates dans SQL en fonction du mois et de l'année uniquement.

Nous effectuons des calculs en fonction des dates et puisque la facturation a lieu sur une base mensuelle, la date du mois a causé plus d'obstacles.

Par exemple

DECLARE @date1 DATETIME = CAST('6/15/2014' AS DATETIME),
        @date2 DATETIME = CAST('6/14/2014' AS DATETIME)

SELECT * FROM tableName WHERE @date1 <= @date2

L'exemple ci-dessus ne retournerait aucune ligne car @ date1 est supérieur à @ date2. J'aimerais donc trouver un moyen de sortir la journée de l'équation.

De même, la situation suivante me donne du chagrin pour la même raison.

DECLARE @date1 DATETIME = CAST('6/14/2014' AS DATETIME),
        @date2 DATETIME = CAST('6/15/2014' AS DATETIME),
        @date3 DATETIME = CAST('7/1/2014' AS DATETIME)

SELECT * FROM tableName WHERE @date2 BETWEEN @date1 AND @date3

J'ai effectué des conversions en ligne des dates pour dériver le premier jour et le dernier jour du mois pour la date spécifiée.

SELECT * 
FROM tableName 
WHERE date2 BETWEEN
    DATEADD(month, DATEDIFF(month, 0, date1), 0) -- The first day of the month for date1
    AND
    DATEADD(s, -1, DATEADD(mm, DATEDIFF(m, 0, date2) + 1, 0)) -- The lastday of the month for date3

Il doit y avoir un moyen plus simple de le faire. Aucune suggestion?

18
Andy Evans

Pour gérer les inégalités, telles qu'entre les deux, j'aime convertir la date/l'heure en une représentation YYYYMM, sous forme de chaîne ou d'entier. Pour cet exemple:

DECLARE @date1 DATETIME = CAST('6/14/2014' AS DATETIME),
        @date2 DATETIME = CAST('6/15/2014' AS DATETIME),
        @date3 DATETIME = CAST('7/1/2014' AS DATETIME);

SELECT * FROM tableName WHERE @date2 BETWEEN @date1 AND @date3;

J'écrirais la requête comme suit:

SELECT *
FROM tableName
WHERE year(@date2) * 100 + month(@date2) BETWEEN year(@date1) * 100 + month(@date1) AND
                                                 year(@date3) * 100 + month(@date1);
21
Gordon Linoff

Vous pouvez filtrer le mois et l'année d'une date donnée sur la date actuelle comme suit:

SELECT * 
FROM tableName 
WHERE month(date2) = month(getdate()) and year(date2) = year(getdate())

Remplacez simplement la méthode GETDATE() par la date souhaitée.

15
Tanner

Vous pouvez joindre les valeurs MONTH et YEAR de ces dates:

SELECT * 
FROM tableName 
WHERE YEAR(@date1) = YEAR(@date2) AND MONTH(@date1) = MONTH(@date2)
9
DavidG

Tout d'abord, j'utiliserais un format pour les dates sans ambiguïté, comme le standard 'YYYYMMDD' Et non le '6/15/2014' Que vous avez utilisé. Le blog d'Aaron Bertrand explique bien mieux que moi les différentes manières dont cela peut mal tourner:
Mauvaises habitudes de lancer: mauvaise gestion des requêtes de date/plage

Pour le problème spécifique, votre dernière requête qui trouve le premier et le dernier jour des mois (pour date1 et date3), est à mon avis sur la bonne voie. Vous n'avez besoin que des premiers jours des mois (premier jour de date1 et premier jour du mois suivant pour date3), si vous évitez le evil BETWEEN: Qu'ont en commun BETWEEN et le diable?

SELECT * 
FROM tableName 
WHERE date2 >= DATEADD(month, DATEDIFF(month, '19000101', @date1), '19000101')
  AND date2 < DATEADD(month, 1+DATEDIFF(month, '19000101', @date3), '19000101') ;

La requête fonctionne telle quelle, quel que soit le type de données de date2 (DATE, DATETIME, DATETIME2 Ou SMALLDATTEIME).

Point bonus, les index sur date2 Seront ainsi considérés par l'optimiseur.


Amélioration, selon (encore un autre) article de blog d'Aaron, pour éviter un problème d'estimation de la cardinalité lors de l'évaluation des expressions avec DATEDIFF():
Surprises et hypothèses de performance: DATEDIFF

SELECT * 
FROM tableName 
WHERE date2 >= CONVERT(DATE, DATEADD(day, 1 - DAY(@date1), @date1))
  AND date2 < DATEADD(month, 1, 
                      CONVERT(DATE, DATEADD(day, 1 - DAY(@date3), @date3))) ;
8
ypercubeᵀᴹ

Vous pouvez utiliser le formatage de votre date comme ceci "aaaaMM", donc seul le mois de la même année sera sélectionné.

SELECT * 
FROM tableName 
WHERE FORMAT(date_month_bill, 'yyyyMM') < FORMAT(DATEADD(MONTH, -1, GETDATE()), 'yyyyMM') 
AND FORMAT(date_month_bill, 'yyyyMM') > FORMAT(DATEADD(MONTH, -3, GETDATE()), 'yyyyMM')
0
Andy Ferdian