sql >> Databasteknik >  >> RDS >> Sqlserver

En lösning för att DATEDIFF() ignorerar SET DATEFIRST i SQL Server (T-SQL-exempel)

En intressant sak med DATEDIFF() funktion i SQL Server är att den ignorerar din SET DATEFIRST värde.

Detta är dock ingen bugg. Microsofts dokumentation för DATEDIFF() anger tydligt följande:

Ange SET DATEFIRST har ingen effekt på DATEDIFF . DATEDIFF använder alltid söndag som den första dagen i veckan för att säkerställa att funktionen fungerar på ett deterministiskt sätt.

Om du inte vet, SET DATEFIRST ställer in den första dagen i veckan för din session. Det är ett nummer från 1 till 7 (vilket motsvarar måndag till söndag).

Det initiala värdet för SET DATEFIRST ställs implicit av språkinställningen (som du kan ställa in med SET LANGUAGE påstående). Det faktiska värdet beror på vilket språk som är inställt. Till exempel standardvärdet för us_english språket är 7 (söndag), medan standard för British språket är 1 (måndag).

Du kan dock använda en SET DATEFIRST för att åsidosätta detta så att du kan fortsätta använda samma språk samtidigt som du använder en annan dag den första dagen i veckan.

Men som nämnt, SET DATEFIRST värdet har ingen effekt på DATEDIFF() fungera. DATEDIFF() funktionen förutsätter alltid att söndag är den första dagen i veckan oavsett din SET DATEFIRST värde.

Detta kan orsaka några intressanta problem när du använder DATEDIFF() om du inte vet hur det fungerar.

Om du hamnar i den här situationen kan förhoppningsvis exemplen på den här sidan hjälpa dig.

Exempel 1 – Problemet

Först, här är ett exempel på det faktiska problemet. Observera att vi kan hämta SET DATEFIRST värde genom att välja @@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ärde | us_english DATEDIFF() Resultat ||----------------------------+------------------- ------------|| 7 | 0 |+------------------------+---------------------- ----------++------------------------+--------- --------------+| SET DATEFIRST Värde | Brittisk DATEDIFF() Resultat ||-----------------------------+------------------- ----------|| 1 | 0 |+------------------------+---------------------- ------+

I det här fallet infaller det första datumet på en söndag och det andra datumet på en måndag. Därför skulle du normalt förvänta dig den brittiska DATEDIFF() resultat för att returnera 1 . Du kan förvänta dig detta eftersom gränsen för veckodel korsas när den går från söndag till måndag (eftersom SET DATEFIRST värdet är 1 vilket betyder "måndag", och måndag markerar början på en ny vecka).

Men eftersom DATEDIFF() ignorerar din SET DATEFIRST värde och antar att söndag är början på veckan, får vi samma resultat för båda språken.

Bara för att vara säker kör jag frågan igen, men den här gången ställer jag in SET DATEFIRST värde explicit . Med andra ord, istället för att ställa in språket använder jag SET DATEFIRST uttalande:

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';

Resultat:

+-------------------------+----------------------- ----------+| SET DATEFIRST Värde | us_english DATEDIFF() Resultat ||----------------------------+------------------- ------------|| 7 | 0 |+------------------------+---------------------- ----------++------------------------+--------- --------------+| SET DATEFIRST Värde | Brittisk DATEDIFF() Resultat ||-----------------------------+------------------- ----------|| 1 | 0 |+------------------------+---------------------- ------+

Samma resultat, även när du uttryckligen ställer in SET DATEFIRST värde. Detta är dock ingen överraskning – jag skulle bli förvånad om det inte gjorde det returnera samma resultat.

Dessutom bekräftar detta helt enkelt att DATEDIFF() fungerar precis som avsett.

Så, hur ändrar vi det så att vår DATEDIFF() resultat hedrar vår SET DATEFIRST värde?

Lösningen

Här är en lösning/lösning som gör att du kan få de avsedda resultaten. Detta säkerställer att din SET DATEFIRST inställningarna tas med i din DATEDIFF() resultat.

Allt du behöver göra är att subtrahera @@DATEFIRST från inmatningsdatumen.

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 SOM 'SET DATEFIRST Value', DATEDIFF(week, DATEADD(day, [email protected]@DATEFIRST, @startdate), DATEADD(day, [email protected]@DATEFIRST, @enddate)) AS 'British DATEDIFF() Result'; 

Resultat:

+-------------------------+----------------------- ----------+| SET DATEFIRST Värde | us_english DATEDIFF() Resultat ||----------------------------+------------------- ------------|| 7 | 0 |+------------------------+---------------------- ----------++------------------------+--------- --------------+| SET DATEFIRST Värde | Brittisk DATEDIFF() Resultat ||-----------------------------+------------------- ----------|| 1 | 1 |+------------------------+---------------------- ------+

Detta använder DATEADD() funktion för att minska inmatningsdatumen med mängden @@DATEFIRST (vilket är din SET DATEFIRST värde).

I det här fallet DATEDIFF() funktionen använder fortfarande söndag som första dag i veckan, men de faktiska datumen som används i beräkningen är olika. De har flyttats tillbaka i tiden med mängden @@DATEFIRST .

Följande exempel visar datumen som användes i beräkningen:

DECLARE @startdate date ='2025-01-05', @enddate date ='2025-01-06';SET DATEFIRST 7;VÄLJ @startdatum SOM 'Original Date', @@DATEFIRST SOM 'Subtrahera av', DATEADD(day, [email protected]@DATEFIRST, @startdate) AS 'Resulting Date'UNION ALLSELECT @enddate, @@DATEFIRST, DATEADD(day, [email protected]@DATEFIRST, @slutdatum); SET DATEFIRST 1;VÄLJ @startdatum SOM 'Original Date', @@DATEFIRST SOM 'Subtrahera av', DATEADD(dag, [email protected]@DATEFIRST, @startdatum) SOM 'Resultatdatum'UNION ALLSELECT @slutdatum, @@DATEFIRST , DATEADD(dag, [email protected]@DATEFIRST, @slutdatum); 

Resultat:

+----------------+--------------+---------------- ------+| Originaldatum | Subtrahera med | Resultatdatum ||----------------+--------------+------------ ------|| 2025-01-05 | 7 | 2024-12-29 || 2025-01-06 | 7 | 2024-12-30 |+----------------+--------------------+--------- ----------++----------------+--------------+----- -------------+| Originaldatum | Subtrahera med | Resultatdatum ||----------------+--------------+------------ ------|| 2025-01-05 | 1 | 2025-01-04 || 2025-01-06 | 1 | 2025-01-05 |+----------------+-------------------+-------- ----------+

Så i vår lösning, DATEDIFF() använde "Resulterande datum" i sina beräkningar.

Om du har stött på problem med DATEDIFF() ignorerar SET DATEFIRST , förhoppningsvis hjälpte den här artikeln.


  1. Hur använder man Partition By eller Max?

  2. Mallmönster och modifierare för formatering av datum/tid i PostgreSQL

  3. Multiplikationsaggregatoperator i SQL

  4. Hur kan jag få en lista över alla funktioner lagrade i databasen för ett visst schema i PostgreSQL?