Интересно нещо за DATEDIFF()
функция в SQL Server е, че той игнорира вашия SET DATEFIRST
стойност.
Това обаче не е грешка. Документацията на Microsoft за DATEDIFF()
ясно посочва следното:
Посочване на
SET DATEFIRST
няма ефект върхуDATEDIFF
.DATEDIFF
винаги използва неделя като първи ден от седмицата, за да гарантира, че функцията работи по детерминиран начин.
В случай, че не знаете, SET DATEFIRST
задава първия ден от седмицата за вашата сесия. Това е число от 1 до 7 (което съответства на понеделник до неделя).
Първоначалната стойност за SET DATEFIRST
се задава имплицитно от настройката за език (която можете да зададете с SET LANGUAGE
изявление). Действителната стойност ще зависи от езика, който е зададен. Например стойността по подразбиране за us_english
езикът е 7
(неделя), докато по подразбиране за British
езикът е 1
(понеделник).
Можете обаче да използвате SET DATEFIRST
изявление, за да замените това, за да можете да продължите да използвате същия език, докато използвате различен ден за първия ден от седмицата.
Но както споменахме, SET DATEFIRST
стойността няма ефект върху DATEDIFF()
функция. DATEDIFF()
функцията винаги приема, че неделя е първият ден от седмицата, независимо от SET DATEFIRST
стойност.
Това може да причини някои интересни проблеми при използване на DATEDIFF()
ако не знаете как работи.
Ако се окажете в тази ситуация, надявам се примерите на тази страница да ви помогнат.
Пример 1 – Проблемът
Първо, ето пример за действителния проблем. Имайте предвид, че можем да извлечем SET DATEFIRST
стойност, като изберете @@DATEFIRST
.
DECLARE @startdate date ='2025-01-05', @enddate date ='2025-01-06';ЗАДАДЕТЕ ЕЗИК us_english;ИЗБЕРЕТЕ @@DATEFIRST КАТО 'ЗАДАВАНЕ НА DATEFIRST стойност', DATEDIFF(седмица, @startdate, @enddate) КАТО 'us_english DATEDIFF() Резултат';ЗАДАДЕТЕ ЕЗИК британски;ИЗБЕРЕТЕ @@DATEFIRST КАТО 'ЗАДАВАНЕ НА DATEFIRST стойност', DATEDIFF(седмица, @startdate, @enddate) КАТО 'британски DATEDIFF() резултат';
Резултат:
+-----------------------+--------------------- ----------+| SET DATEFIRST Стойност | us_english DATEDIFF() Резултат ||-----------------------+------------------- ------------|| 7 | 0 |+-----------------------+----------------------- ---------++-----------------------+-------------- --------------+| SET DATEFIRST Стойност | Британски DATEDIFF() Резултат ||-----------------------+------------------ ----------|| 1 | 0 |+-----------------------+----------------------- ------+
В този случай първата дата е в неделя, а втората в понеделник. Следователно обикновено очаквате британския DATEDIFF()
резултат за връщане на 1
. Бихте очаквали това, защото границата на седмичната част се пресича, когато преминава от неделя до понеделник (защото SET DATEFIRST
стойността е 1
което означава „понеделник“, а понеделник бележи началото на нова седмица).
Но тъй като DATEDIFF()
игнорира вашия SET DATEFIRST
стойност и приема, че неделя е началото на седмицата, получаваме същия резултат и за двата езика.
Само за да съм сигурен, ще изпълня заявката отново, но този път ще задам SET DATEFIRST
стойност изрично . С други думи, вместо да задавам езика, ще използвам SET DATEFIRST
изявление:
DECLARE @startdate date ='2025-01-05', @enddate date ='2025-01-06';ЗАДАДЕТЕ DATEFIRST 7;ИЗБЕРЕТЕ @@DATEFIRST КАТО 'ЗАДАВАНЕ НА DATEFIRST стойност', DATEDIFF(седмица, @startdate, @enddate) AS 'us_english DATEDIFF() Result';SET DATEFIRST 1;SELECT @@DATEFIRST AS 'SET DATEFIRST Value', DATEDIFF(week, @startdate, @enddate) AS 'British DATEDIFF() Result';
Резултат:
+-----------------------+--------------------- ----------+| SET DATEFIRST Стойност | us_english DATEDIFF() Резултат ||-----------------------+------------------- ------------|| 7 | 0 |+-----------------------+----------------------- ---------++-----------------------+-------------- --------------+| SET DATEFIRST Стойност | Британски DATEDIFF() Резултат ||-----------------------+------------------ ----------|| 1 | 0 |+-----------------------+----------------------- ------+
Същият резултат, дори когато изрично зададете SET DATEFIRST
стойност. Това обаче не е изненада – бих се изненадал, ако не беше върнете същия резултат.
Също така, това просто потвърждава, че DATEDIFF()
работи точно както е предвидено.
И така, как да го променим така, че нашият DATEDIFF()
резултатите отговарят на нашия SET DATEFIRST
стойност?
Решението
Ето решение/заобиколно решение, което ще ви позволи да получите очакваните резултати. Това ще гарантира, че вашият SET DATEFIRST
настройките са включени във вашия DATEDIFF()
резултати.
Всичко, което трябва да направите, е да извадите @@DATEFIRST
от въведените дати.
DECLARE @startdate date ='2025-01-05', @enddate date ='2025-01-06';ЗАДАДЕТЕ DATEFIRST 7;ИЗБЕРЕТЕ @@DATEFIRST КАТО 'ЗАДАВАНЕ НА DATEFIRST стойност', DATEDIFF(седмица, DATEADD(ден , [email protected]@DATEFIRST, @startdate), DATEADD(day, [email protected]@DATEFIRST, @enddate)) AS 'us_english DATEDIFF() Result';SET DATEFIRST 1;SELECT @@DATEFIRST AS 'SET DATEFIRST Стойност“, DATEDIFF(седмица, DATEADD(ден, [email protected]@DATEFIRST, @startdate), DATEADD(ден, [email protected]@DATEFIRST, @enddate)) КАТО „британски резултат DATEDIFF()“;предварително>Резултат:
+-----------------------+--------------------- ----------+| SET DATEFIRST Стойност | us_english DATEDIFF() Резултат ||-----------------------+------------------- ------------|| 7 | 0 |+-----------------------+----------------------- ---------++-----------------------+-------------- --------------+| SET DATEFIRST Стойност | Британски DATEDIFF() Резултат ||-----------------------+------------------ ----------|| 1 | 1 |+-----------------------+----------------------- ------+Това използва
DATEADD()
функция за намаляване на въведените дати с количеството@@DATEFIRST
(което е вашиятSET DATEFIRST
стойност).В този случай
DATEDIFF()
функцията все още използва неделя като първи ден от седмицата, но действителните дати, използвани в изчислението, са различни. Те са преместени назад във времето с количеството@@DATEFIRST
.Следният пример показва датите, използвани при изчислението:
DECLARE @startdate date ='2025-01-05', @enddate date ='2025-01-06';ЗАДАДЕТЕ DATEFIRST 7;ИЗБЕРЕТЕ @startdate КАТО 'Оригинална дата', @@DATEFIRST КАТО 'Извадете по', DATEADD(ден, пример@sqldat.com@DATEFIRST, @startdate) КАТО 'Резултна дата'UNION ALLSELECT @enddate, @@DATEFIRST, DATEADD(ден, [email protected]@DATEFIRST, @enddate); ЗАДАЙТЕ DATEFIRST 1;ИЗБЕРЕТЕ @startdate КАТО 'Оригинална дата', @@DATEFIRST КАТО 'Извадете по', DATEADD(ден, [email protected]@DATEFIRST, @startdate) КАТО 'Резултна дата'UNION ALLSELECT @enddate, @@DATEFIRST , DATEADD(ден, пример@sqldat.com@DATEFIRST, @крайна дата);Резултат:
+-----------------+--------------+----------- ------+| Оригинална дата | Изваждане по | Резултатна дата ||-----------------+--------------+------------ ------|| 2025-01-05 | 7 | 29.12.2024 || 2025-01-06 | 7 | 2024-12-30 |+----------------+--------------+-------- ---------++----------------+--------------+----- ------------+| Оригинална дата | Изваждане по | Резултатна дата ||-----------------+--------------+------------ ------|| 2025-01-05 | 1 | 2025-01-04 || 2025-01-06 | 1 | 2025-01-05 |+----------------+--------------+-------- ---------+Така че в нашето заобиколно решение,
DATEDIFF()
използва „Резултатна дата“ в своите изчисления.Ако сте срещали проблеми с
DATEDIFF()
игнорирайкиSET DATEFIRST
, надявам се тази статия да е помогнала.