sql >> Databáze >  >> RDS >> Sqlserver

Řešení pro DATEDIFF() Ignorování SET DATEFIRST v SQL Server (příklad T-SQL)

Zajímavá věc o DATEDIFF() funkce v SQL Server je, že ignoruje váš SET DATEFIRST hodnota.

To však není chyba. Dokumentace společnosti Microsoft pro DATEDIFF() jasně uvádí následující:

Zadání SET DATEFIRST nemá žádný vliv na DATEDIFF . DATEDIFF vždy používá neděli jako první den v týdnu, aby bylo zajištěno, že funkce funguje deterministickým způsobem.

V případě, že nevíte, SET DATEFIRST nastaví první den v týdnu pro vaši relaci. Je to číslo od 1 do 7 (což odpovídá pondělí až neděli).

Počáteční hodnota pro SET DATEFIRST je implicitně nastaveno nastavením jazyka (které můžete nastavit pomocí SET LANGUAGE prohlášení). Skutečná hodnota bude záviset na nastaveném jazyce. Například výchozí hodnota pro us_english jazyk je 7 (neděle), zatímco výchozí pro British jazyk je 1 (Pondělí).

Můžete však použít SET DATEFIRST toto přepíšete, abyste mohli nadále používat stejný jazyk a zároveň používat jiný den pro první den v týdnu.

Ale jak již bylo zmíněno, SET DATEFIRST hodnota nemá žádný vliv na DATEDIFF() funkce. DATEDIFF() funkce vždy předpokládá, že neděle je prvním dnem v týdnu bez ohledu na SET DATEFIRST hodnotu.

To může způsobit zajímavé problémy při použití DATEDIFF() pokud nevíte, jak to funguje.

Pokud se ocitnete v této situaci, doufejme, že příklady na této stránce vám pomohou.

Příklad 1 – Problém

Nejprve je zde příklad skutečného problému. Všimněte si, že můžeme načíst SET DATEFIRST hodnotu výběrem @@DATEFIRST .

DECLARE @startdate date ='2025-01-05', @enddate date ='2025-01-06';SET LANGUAGE us_english;SELECT @@DATEFIRST JAKO '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';

Výsledek:

+-----------------------+---------------------- ----------+| SET DATEFIRST Hodnota | us_english DATEDIFF() Výsledek ||-----------------------+------------------- -------------|| 7 | 0 |+-----------------------+------------------------ ---------++-----------------------+--------------- ---------------+| SET DATEFIRST Hodnota | Britský výsledek DATEDIFF() ||-----------------------+------------------- ----------|| 1 | 0 |+-----------------------+------------------------ ------+

V tomto případě první datum připadá na neděli a druhé datum na pondělí. Proto byste normálně očekávali britské DATEDIFF() výsledek vrátí 1 . Očekávali byste to, protože hranice týdenní části je překročena, když jde od neděle do pondělí (protože SET DATEFIRST hodnota je 1 což znamená „pondělí“ a pondělí znamená začátek nového týdne).

Ale protože DATEDIFF() ignoruje váš SET DATEFIRST a předpokládá, že neděle je začátkem týdne, dostaneme stejný výsledek pro oba jazyky.

Pro jistotu spustím dotaz znovu, ale tentokrát nastavím SET DATEFIRST hodnotu explicitně . Jinými slovy, místo nastavení jazyka použiji SET DATEFIRST prohlášení:

DECLARE @startdate date ='2025-01-05', @enddate date ='2025-01-06';SET DATEFIRST 7;SELECT @@DATEFIRST JAKO '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';

Výsledek:

+-----------------------+---------------------- ----------+| SET DATEFIRST Hodnota | us_english DATEDIFF() Výsledek ||-----------------------+------------------- -------------|| 7 | 0 |+-----------------------+------------------------ ---------++-----------------------+--------------- ---------------+| SET DATEFIRST Hodnota | Britský výsledek DATEDIFF() ||-----------------------+------------------- ----------|| 1 | 0 |+-----------------------+------------------------ ------+

Stejný výsledek, i když explicitně nastavíte SET DATEFIRST hodnota. To však není žádné překvapení – byl bych překvapen, kdyby nebylo vrátit stejný výsledek.

Také to jednoduše potvrzuje, že DATEDIFF() funguje přesně tak, jak bylo zamýšleno.

Jak to tedy změníme, aby naše DATEDIFF() výsledky respektují naše SET DATEFIRST hodnotu?

Řešení

Zde je řešení/obcházení, které vám umožní dosáhnout zamýšlených výsledků. Tím zajistíte, že SET DATEFIRST nastavení jsou zohledněna ve vašem DATEDIFF() Výsledek.

Vše, co musíte udělat, je odečíst @@DATEFIRST ze vstupních dat.

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

Výsledek:

+-----------------------+---------------------- ----------+| SET DATEFIRST Hodnota | us_english DATEDIFF() Výsledek ||-----------------------+------------------- -------------|| 7 | 0 |+-----------------------+------------------------ ---------++-----------------------+--------------- ---------------+| SET DATEFIRST Hodnota | Britský výsledek DATEDIFF() ||-----------------------+------------------- ----------|| 1 | 1 |+-----------------------+------------------------ ------+

Toto používá DATEADD() funkce ke snížení vstupních dat o hodnotu @@DATEFIRST (což je vaše SET DATEFIRST hodnota).

V tomto případě DATEDIFF() funkce stále používá neděli jako první den v týdnu, avšak skutečná data použitá ve výpočtu se liší. Byly posunuty zpět v čase o hodnotu @@DATEFIRST .

Následující příklad ukazuje data, která byla použita při výpočtu:

DECLARE @startdate date ='2025-01-05', @enddate date ='2025-01-06';SET DATEFIRST 7;SELECT @startdate AS 'Původní datum', @@DATEFIRST AS 'Odečíst do', DATEADD(den, [email protected]@DATEFIRST, @startdate) AS 'Výsledné datum'UNION ALLSELECT @enddate, @@DATEFIRST, DATEADD(den, [email protected]@DATEFIRST, @enddate); SET DATEFIRST 1;SELECT @startdate AS 'Původní datum', @@DATEFIRST AS 'Odečíst do', DATEADD(den, pří[email protected]@DATEFIRST, @startdate) JAKO 'Výsledné datum'UNION ALLSELECT @enddate, DATEFIRST , DATEADD(den, [email protected]@DATEFIRST, @enddate); 

Výsledek:

+-----------------+---------------+------------- ------+| Původní datum | Odečíst od | Výsledné datum ||-----------------+---------------+------------ ------|| 2025-01-05 | 7 | 2024-12-29 || 2025-01-06 | 7 | 2024-12-30 |+-----------------+---------------+---------- ---------++-----------------+----------------+----- -------------+| Původní datum | Odečíst od | Výsledné datum ||-----------------+---------------+------------ ------|| 2025-01-05 | 1 | 2025-01-04 || 2025-01-06 | 1 | 2025-01-05 |+-----------------+---------------+---------- ---------+

Takže v našem řešení DATEDIFF() ve svých výpočtech použila „Výsledné datum“.

Pokud jste narazili na problémy s DATEDIFF() ignorování SET DATEFIRST , doufám, že vám tento článek pomohl.


  1. Jak vytvoříte verzi schématu databáze?

  2. Autoincrement v oracle do již vytvořené tabulky

  3. Nejlepší způsob, jak nainstalovat hstore na více schémat v databázi Postgres?

  4. Oprava Msg 8116 „Datový typ argumentu varchar je neplatný pro argument 1 funkce session_context“ v SQL Server