sql >> Database teknologi >  >> RDS >> Sqlserver

En løsning til DATEDIFF() ignorering af SET DATEFIRST i SQL Server (T-SQL-eksempel)

En interessant ting ved DATEDIFF() funktion i SQL Server er, at den ignorerer din SET DATEFIRST værdi.

Dette er dog ikke en fejl. Microsofts dokumentation for DATEDIFF() står klart følgende:

Angivelse af SET DATEFIRST har ingen effekt på DATEDIFF . DATEDIFF bruger altid søndag som den første dag i ugen for at sikre, at funktionen fungerer på en deterministisk måde.

I tilfælde af at du ikke ved det, SET DATEFIRST indstiller den første dag i ugen for din session. Det er et tal fra 1 til 7 (hvilket svarer til mandag til søndag).

Startværdien for SET DATEFIRST er implicit indstillet af sprogindstillingen (som du kan indstille med SET LANGUAGE udmelding). Den faktiske værdi afhænger af det indstillede sprog. For eksempel standardværdien for us_english sproget er 7 (søndag), mens standarden for British sproget er 1 (Mandag).

Du kan dog bruge en SET DATEFIRST erklæring for at tilsidesætte dette, så du kan fortsætte med at bruge det samme sprog, mens du bruger en anden dag på den første dag i ugen.

Men som nævnt er SET DATEFIRST værdi har ingen effekt på DATEDIFF() fungere. DATEDIFF() funktionen antager altid, at søndag er den første dag i ugen uanset din SET DATEFIRST værdi.

Dette kan forårsage nogle interessante problemer, når du bruger DATEDIFF() hvis du ikke ved, hvordan det virker.

Hvis du befinder dig i denne situation, kan eksemplerne på denne side forhåbentlig hjælpe.

Eksempel 1 – Problemet

For det første er her et eksempel på det faktiske problem. Bemærk, at vi kan hente SET DATEFIRST værdi ved at vælge @@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';

Resultat:

+------------------------+---------- ----------+| SET DATEFIRST Værdi | us_english DATEDIFF() Resultat ||----------------------------+------------------------ --------------|| 7 | 0 |+------------------------+---------- -----------++-------------------------+--------- --------------+| SET DATEFIRST Værdi | Britisk DATEDIFF() Resultat ||-----------------------------+------------------------ ----------|| 1 | 0 |+------------------------+---------- ------+

I dette tilfælde falder den første dato på en søndag og den anden dato på en mandag. Derfor ville du normalt forvente den britiske DATEDIFF() resultat for at returnere 1 . Du ville forvente dette, fordi uge-del-grænsen krydses, når den går fra søndag til mandag (fordi SET DATEFIRST værdien er 1 hvilket betyder "mandag", og mandag markerer starten på en ny uge).

Men fordi DATEDIFF() ignorerer din SET DATEFIRST værdi og antager, at søndag er starten på ugen, får vi det samme resultat for begge sprog.

For at være sikker kører jeg forespørgslen igen, men denne gang indstiller jeg SET DATEFIRST værdi eksplicit . Med andre ord, i stedet for at indstille sproget, bruger jeg SET DATEFIRST erklæring:

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

Resultat:

+------------------------+---------- ----------+| SET DATEFIRST Værdi | us_english DATEDIFF() Resultat ||----------------------------+------------------------ --------------|| 7 | 0 |+------------------------+---------- -----------++-------------------------+--------- --------------+| SET DATEFIRST Værdi | Britisk DATEDIFF() Resultat ||-----------------------------+------------------------ ----------|| 1 | 0 |+------------------------+---------- ------+

Samme resultat, selv når du udtrykkeligt indstiller SET DATEFIRST værdi. Dette er dog ingen overraskelse – jeg ville blive overrasket, hvis det ikke gjorde returnere det samme resultat.

Dette bekræfter også blot, at DATEDIFF() fungerer nøjagtigt efter hensigten.

Så hvordan ændrer vi det, så vores DATEDIFF() resultater ære vores SET DATEFIRST værdi?

Løsningen

Her er en løsning/løsning, der giver dig mulighed for at få de tilsigtede resultater. Dette vil sikre, at din SET DATEFIRST indstillinger er indregnet i din DATEDIFF() resultater.

Alt du skal gøre er at trække @@DATEFIRST fra fra inputdatoerne.

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

Resultat:

+------------------------+---------- ----------+| SET DATEFIRST Værdi | us_english DATEDIFF() Resultat ||----------------------------+------------------------ --------------|| 7 | 0 |+------------------------+---------- -----------++-------------------------+--------- --------------+| SET DATEFIRST Værdi | Britisk DATEDIFF() Resultat ||-----------------------------+------------------------ ----------|| 1 | 1 |+------------------------+---------- ------+

Dette bruger DATEADD() funktion til at reducere inputdatoer med mængden @@DATEFIRST (som er din SET DATEFIRST værdi).

I dette tilfælde DATEDIFF() funktion bruger stadig søndag som den første dag i ugen, dog er de faktiske datoer, der bruges i beregningen, forskellige. De er blevet flyttet tilbage i tiden med mængden @@DATEFIRST .

Følgende eksempel viser de datoer, der blev brugt i beregningen:

DECLARE @startdate date ='2025-01-05', @enddate date ='2025-01-06';SET DATEFIRST 7;SELECT @startdate AS 'Original Date', @@DATEFIRST AS 'Subtract By', DATEADD(dag, [email protected]@DATEFIRST, @startdato) AS 'Resultatdato'UNION ALLSELECT @slutdato, @@DATOFIRST, DATEADD(dag, [email protected]@DATEFIRST, @slutdato); INDSTIL DATEFIRST 1;VÆLG @startdato SOM 'Original Dato', @@DATEFIRST SOM 'Subtract By', DATEADD(dag, [email protected]@DATEFIRST, @startdato) SOM 'Resultatdato'UNION ALLSELECT @slutdato, @@DATEFIRST , DATEADD(dag, [email protected]@DATEFIRST, @slutdato); 

Resultat:

+----------------+----------------- ------+| Original dato | Træk fra ved | Resultatdato ||----------------+--------------+------------ ------|| 05-01-2025 | 7 | 29-12-2024 || 06-01-2025 | 7 | 30-12-2024 |+----------------+----------------- ----------++----------------+--------------+----- --------------+| Original dato | Træk fra ved | Resultatdato ||----------------+--------------+------------ ------|| 2025-01-05 | 1 | 04-01-2025 || 06-01-2025 | 1 | 2025-01-05 |+----------------+------------------+-------- ----------+

Så i vores løsning, DATEDIFF() brugte "Resultatdatoen" i sine beregninger.

Hvis du er løbet ind i problemer med DATEDIFF() ignorerer SET DATEFIRST , forhåbentlig hjalp denne artikel.


  1. Hvordan viser jeg alle tabeller i et skema i Oracle SQL?

  2. Vælg sidste række i MySQL

  3. SQL Server-pivot vs. multiple join

  4. Adressering af Drop Column Bug i Oracle 18c og 19c