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 dinSET 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()
ignorerarSET DATEFIRST
, förhoppningsvis hjälpte den här artikeln.