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

Una soluzione alternativa per DATEDIFF() ignorando SET DATEFIRST in SQL Server (esempio T-SQL)

Una cosa interessante su DATEDIFF() funzione in SQL Server è che ignora il tuo SET DATEFIRST valore.

Tuttavia, questo non è un bug. Documentazione Microsoft per DATEDIFF() afferma chiaramente quanto segue:

Specificando SET DATEFIRST non ha effetto su DATEDIFF . DATEDIFF utilizza sempre la domenica come primo giorno della settimana per garantire che la funzione operi in modo deterministico.

Nel caso non lo sapessi, SET DATEFIRST imposta il primo giorno della settimana per la tua sessione. È un numero da 1 a 7 (che corrisponde a lunedì a domenica).

Il valore iniziale per SET DATEFIRST è implicitamente impostato dall'impostazione della lingua (che puoi impostare con il SET LANGUAGE dichiarazione). Il valore effettivo dipenderà dalla lingua impostata. Ad esempio il valore predefinito per us_english la lingua è 7 (domenica), mentre il valore predefinito per il British la lingua è 1 (Lunedi).

Tuttavia, puoi utilizzare un SET DATEFIRST istruzione per ignorarlo in modo da poter continuare a utilizzare la stessa lingua mentre si utilizza un giorno diverso per il primo giorno della settimana.

Ma come accennato, il SET DATEFIRST value non ha effetto su DATEDIFF() funzione. Il DATEDIFF() la funzione presuppone sempre che la domenica sia il primo giorno della settimana indipendentemente dal tuo SET DATEFIRST valore.

Ciò può causare alcuni problemi interessanti quando si utilizza DATEDIFF() se non sai come funziona.

Se ti trovi in ​​questa situazione, si spera che gli esempi in questa pagina possano aiutarti.

Esempio 1 – Il problema

Innanzitutto, ecco un esempio del problema reale. Nota che possiamo recuperare il SET DATEFIRST valore selezionando @@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';

Risultato:

+-----------------------+--------------------------------+
| SET DATEFIRST Value   | us_english DATEDIFF() Result   |
|-----------------------+--------------------------------|
| 7                     | 0                              |
+-----------------------+--------------------------------+

+-----------------------+-----------------------------+
| SET DATEFIRST Value   | British DATEDIFF() Result   |
|-----------------------+-----------------------------|
| 1                     | 0                           |
+-----------------------+-----------------------------+

In questo caso, la prima data cade di domenica e la seconda data di lunedì. Quindi normalmente ti aspetteresti il ​​DATEDIFF() britannico risultato per restituire 1 . Te lo aspetteresti perché il limite della parte settimanale viene superato quando va da domenica a lunedì (perché il SET DATEFIRST il valore è 1 che significa "lunedì" e lunedì segna l'inizio di una nuova settimana).

Ma perché DATEDIFF() ignora il tuo SET DATEFIRST value e presuppone che la domenica sia l'inizio della settimana, otteniamo lo stesso risultato per entrambe le lingue.

Per sicurezza, eseguirò di nuovo la query, ma questa volta imposterò SET DATEFIRST valore esplicitamente . In altre parole, invece di impostare la lingua, userò il SET DATEFIRST dichiarazione:

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

Risultato:

+-----------------------+--------------------------------+
| SET DATEFIRST Value   | us_english DATEDIFF() Result   |
|-----------------------+--------------------------------|
| 7                     | 0                              |
+-----------------------+--------------------------------+

+-----------------------+-----------------------------+
| SET DATEFIRST Value   | British DATEDIFF() Result   |
|-----------------------+-----------------------------|
| 1                     | 0                           |
+-----------------------+-----------------------------+

Stesso risultato, anche quando imposti esplicitamente SET DATEFIRST valore. Questa non è una sorpresa però:sarei sorpreso se non lo facesse restituire lo stesso risultato.

Inoltre, questo conferma semplicemente che DATEDIFF() funziona esattamente come previsto.

Quindi, come cambiarlo in modo che il nostro DATEDIFF() i risultati rispettano il nostro SET DATEFIRST valore?

La soluzione

Ecco una soluzione/soluzione alternativa che ti consentirà di ottenere i risultati desiderati. Ciò garantirà che il tuo SET DATEFIRST le impostazioni vengono prese in considerazione nel tuo DATEDIFF() risultati.

Tutto quello che devi fare è sottrarre @@DATEFIRST dalle date di input.

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

Risultato:

+-----------------------+--------------------------------+
| SET DATEFIRST Value   | us_english DATEDIFF() Result   |
|-----------------------+--------------------------------|
| 7                     | 0                              |
+-----------------------+--------------------------------+

+-----------------------+-----------------------------+
| SET DATEFIRST Value   | British DATEDIFF() Result   |
|-----------------------+-----------------------------|
| 1                     | 1                           |
+-----------------------+-----------------------------+

Questo utilizza il DATEADD() funzione per ridurre le date di input dell'importo di @@DATEFIRST (che è il tuo SET DATEFIRST valore).

In questo caso il DATEDIFF() la funzione utilizza ancora la domenica come primo giorno della settimana, tuttavia le date effettive utilizzate nel calcolo sono diverse. Sono stati spostati indietro nel tempo della quantità di @@DATEFIRST .

L'esempio seguente mostra le date utilizzate nel calcolo:

DECLARE 
  @startdate date = '2025-01-05', 
  @enddate date = '2025-01-06';

SET DATEFIRST 7;
SELECT 
  @startdate AS 'Original Date', 
  @@DATEFIRST AS 'Subtract By',
  DATEADD(day, [email protected]@DATEFIRST, @startdate) AS 'Resulting Date'
UNION ALL
SELECT 
  @enddate, 
  @@DATEFIRST,
  DATEADD(day, [email protected]@DATEFIRST, @enddate);  

SET DATEFIRST 1;
SELECT 
  @startdate AS 'Original Date', 
  @@DATEFIRST AS 'Subtract By',
  DATEADD(day, [email protected]@DATEFIRST, @startdate) AS 'Resulting Date'
UNION ALL
SELECT 
  @enddate, 
  @@DATEFIRST,
  DATEADD(day, [email protected]@DATEFIRST, @enddate);  

Risultato:

+-----------------+---------------+------------------+
| Original Date   | Subtract By   | Resulting Date   |
|-----------------+---------------+------------------|
| 2025-01-05      | 7             | 2024-12-29       |
| 2025-01-06      | 7             | 2024-12-30       |
+-----------------+---------------+------------------+

+-----------------+---------------+------------------+
| Original Date   | Subtract By   | Resulting Date   |
|-----------------+---------------+------------------|
| 2025-01-05      | 1             | 2025-01-04       |
| 2025-01-06      | 1             | 2025-01-05       |
+-----------------+---------------+------------------+

Quindi nella nostra soluzione alternativa, DATEDIFF() ha utilizzato la “Data risultante” nei suoi calcoli.

Se hai riscontrato problemi con DATEDIFF() ignorando SET DATEFIRST , si spera che questo articolo sia stato di aiuto.