Sqlserver
 sql >> Base de données >  >> RDS >> Sqlserver

Une solution de contournement pour DATEDIFF() ignorant SET DATEFIRST dans SQL Server (exemple T-SQL)

Une chose intéressante à propos du DATEDIFF() fonction dans SQL Server est qu'il ignore votre SET DATEFIRST valeur.

Cependant, ce n'est pas un bug. Documentation de Microsoft pour DATEDIFF() indique clairement ce qui suit :

Spécification de SET DATEFIRST n'a aucun effet sur DATEDIFF . DATEDIFF utilise toujours le dimanche comme premier jour de la semaine pour s'assurer que la fonction fonctionne de manière déterministe.

Si vous ne le savez pas, SET DATEFIRST définit le premier jour de la semaine pour votre session. C'est un nombre de 1 à 7 (ce qui correspond du lundi au dimanche).

La valeur initiale de SET DATEFIRST est défini implicitement par le paramètre de langue (que vous pouvez définir avec le SET LANGUAGE déclaration). La valeur réelle dépendra de la langue définie. Par exemple la valeur par défaut pour le us_english la langue est 7 (dimanche), alors que la valeur par défaut pour le British la langue est 1 (Lundi).

Cependant, vous pouvez utiliser un SET DATEFIRST pour remplacer ceci afin que vous puissiez continuer à utiliser la même langue tout en utilisant un jour différent pour le premier jour de la semaine.

Mais comme mentionné, le SET DATEFIRST la valeur n'a aucun effet sur le DATEDIFF() une fonction. Le DATEDIFF() la fonction suppose toujours que le dimanche est le premier jour de la semaine, quel que soit votre SET DATEFIRST valeur.

Cela peut causer des problèmes intéressants lors de l'utilisation de DATEDIFF() si vous ne savez pas comment cela fonctionne.

Si vous vous trouvez dans cette situation, nous espérons que les exemples de cette page pourront vous aider.

Exemple 1 - Le problème

Tout d'abord, voici un exemple du problème réel. Notez que nous pouvons récupérer le SET DATEFIRST valeur en sélectionnant @@DATEFIRST .

DECLARE @startdate date ='2025-01-05', @enddate date ='2025-01-06';SET LANGUAGE us_english;SELECT @@DATEFIRST AS 'SET DATEFIRST Value', DATEDIFF(week, @startdate, @enddate) AS 'us_english DATEDIFF() Result';SET LANGUAGE British;SELECT @@DATEFIRST AS 'SET DATEFIRST Value', DATEDIFF(week, @startdate, @enddate) AS 'British DATEDIFF() Result';

Résultat :

+-----------------------+---------------------- ----------+| SET DATEFIRST Valeur | us_english DATEDIFF() Result ||-----------------------+------------------- --------------|| 7 | 0 |+-----------------------+----------------------- ---------++-----------------------------------+--------------- --------------+| SET DATEFIRST Valeur | Résultat britannique DATEDIFF() ||----------------------+-------------------------------- ----------|| 1 | 0 |+-----------------------+----------------------- ------+

Dans ce cas, la première date tombe un dimanche et la seconde un lundi. Par conséquent, vous vous attendriez normalement au britannique DATEDIFF() résultat pour renvoyer 1 . Vous vous y attendriez car la limite de la partie semaine est franchie lorsqu'elle va du dimanche au lundi (parce que le SET DATEFIRST la valeur est 1 qui signifie "lundi", et lundi marque le début d'une nouvelle semaine).

Mais parce que DATEDIFF() ignore votre SET DATEFIRST valeur et suppose que dimanche est le début de la semaine, nous obtenons le même résultat pour les deux langues.

Juste pour être sûr, je vais relancer la requête, mais cette fois je vais définir le SET DATEFIRST valeur explicitement . En d'autres termes, au lieu de définir la langue, j'utiliserai le SET DATEFIRST déclaration :

DECLARE @startdate date ='2025-01-05', @enddate date ='2025-01-06';SET DATEFIRST 7;SELECT @@DATEFIRST AS 'SET DATEFIRST Value', DATEDIFF(week, @startdate, @enddate) AS 'us_english DATEDIFF() Result';SET DATEFIRST 1;SELECT @@DATEFIRST AS 'SET DATEFIRST Value', DATEDIFF(week, @startdate, @enddate) AS 'British DATEDIFF() Result';

Résultat :

+-----------------------+---------------------- ----------+| SET DATEFIRST Valeur | us_english DATEDIFF() Result ||-----------------------+------------------- --------------|| 7 | 0 |+-----------------------+----------------------- ---------++-----------------------------------+--------------- --------------+| SET DATEFIRST Valeur | Résultat britannique DATEDIFF() ||----------------------+-------------------------------- ----------|| 1 | 0 |+-----------------------+----------------------- ------+

Même résultat, même lorsque vous définissez explicitement le SET DATEFIRST valeur. Ce n'est pas une surprise cependant - je serais surpris si ce n'était pas retourner le même résultat.

De plus, cela confirme simplement que DATEDIFF() fonctionne exactement comme prévu.

Alors, comment le changer pour que notre DATEDIFF() les résultats respectent notre SET DATEFIRST valeur ?

La solution

Voici une solution/contournement qui vous permettra d'obtenir les résultats escomptés. Cela garantira que votre SET DATEFIRST les paramètres sont pris en compte dans votre DATEDIFF() résultats.

Tout ce que vous avez à faire est de soustraire @@DATEFIRST à partir des dates saisies.

DECLARE @startdate date ='2025-01-05', @enddate date ='2025-01-06';SET DATEFIRST 7;SELECT @@DATEFIRST AS 'SET DATEFIRST Value', DATEDIFF(week, DATEADD(day , [email protected]@DATEFIRST, @startdate), DATEADD(day, [email protected]@DATEFIRST, @enddate)) AS 'us_english DATEDIFF() Result';SET DATEFIRST 1;SELECT @@DATEFIRST AS 'SET DATEFIRST Value', DATEDIFF(week, DATEADD(day, [email protected]@DATEFIRST, @startdate), DATEADD(day, [email protected]@DATEFIRST, @enddate)) AS 'British DATEDIFF() Result'; 

Résultat :

+-----------------------+---------------------- ----------+| SET DATEFIRST Valeur | us_english DATEDIFF() Result ||-----------------------+------------------- --------------|| 7 | 0 |+-----------------------+----------------------- ---------++-----------------------------------+--------------- --------------+| SET DATEFIRST Valeur | Résultat britannique DATEDIFF() ||----------------------+-------------------------------- ----------|| 1 | 1 |+-----------------------+----------------------- ------+

Cela utilise le DATEADD() fonction pour réduire les dates d'entrée du montant de @@DATEFIRST (qui est votre SET DATEFIRST valeur).

Dans ce cas, le DATEDIFF() La fonction utilise toujours le dimanche comme premier jour de la semaine, cependant, les dates réelles utilisées dans le calcul sont différentes. Ils ont été reculés dans le temps d'un montant de @@DATEFIRST .

L'exemple suivant montre les dates qui ont été utilisées dans le calcul :

DECLARE @startdate date ='2025-01-05', @enddate date ='2025-01-06';SET DATEFIRST 7;SELECT @startdate AS 'Date d'origine', @@DATEFIRST AS 'Soustraire par', DATEADD(day, [email protected]@DATEFIRST, @startdate) AS 'Date résultante'UNION ALLSELECT @enddate, @@DATEFIRST, DATEADD(day, [email protected]@DATEFIRST, @enddate); SET DATEFIRST 1;SELECT @startdate AS 'Date d'origine', @@DATEFIRST AS 'Soustraire par', DATEADD(day, [email protected]@DATEFIRST, @startdate) AS 'Date de résultat'UNION ALLSELECT @enddate, @@DATEFIRST , DATEADD(day, [email protected]@DATEFIRST, @enddate); 

Résultat :

+-----------------+---------------+------------ ------+| Date d'origine | Soustraire de | Date de résultat ||-----------------+---------------+-------------------- ------|| 2025-01-05 | 7 | 2024-12-29 || 2025-01-06 | 7 | 2024-12-30 |+-----------------+---------------+--------- ---------++-----------------+---------------+----- --------------+| Date d'origine | Soustraire de | Date de résultat ||-----------------+---------------+-------------------- ------|| 2025-01-05 | 1 | 2025-01-04 || 2025-01-06 | 1 | 2025-01-05 |+-----------------+---------------+--------- ---------+

Donc, dans notre solution de contournement, DATEDIFF() a utilisé la « date de résultat » dans ses calculs.

Si vous rencontrez des problèmes avec DATEDIFF() ignorer SET DATEFIRST , j'espère que cet article vous a aidé.