sql >> Base de Datos >  >> RDS >> Sqlserver

Una solución para DATEDIFF() ignorando SET DATEFIRST en SQL Server (ejemplo de T-SQL)

Algo interesante sobre el DATEDIFF() función en SQL Server es que ignora su SET DATEFIRST valor.

Sin embargo, esto no es un error. Documentación de Microsoft para DATEDIFF() establece claramente lo siguiente:

Especificando SET DATEFIRST no tiene efecto en DATEDIFF . DATEDIFF siempre usa el domingo como el primer día de la semana para garantizar que la función opere de manera determinista.

En caso de que no lo sepas, SET DATEFIRST establece el primer día de la semana para su sesión. Es un número del 1 al 7 (que corresponde de lunes a domingo).

El valor inicial para SET DATEFIRST está establecido implícitamente por la configuración de idioma (que puede establecer con SET LANGUAGE declaración). El valor real dependerá del idioma que se establezca. Por ejemplo, el valor predeterminado para us_english el idioma es 7 (domingo), mientras que el valor predeterminado para el British el idioma es 1 (Lunes).

Sin embargo, puede usar un SET DATEFIRST declaración para anular esto para que pueda seguir usando el mismo idioma mientras usa un día diferente para el primer día de la semana.

Pero como se mencionó, el SET DATEFIRST el valor no tiene efecto en el DATEDIFF() función. El DATEDIFF() La función siempre asume que el domingo es el primer día de la semana, independientemente de su SET DATEFIRST valor.

Esto puede causar algunos problemas interesantes al usar DATEDIFF() si no sabes cómo funciona.

Si te encuentras en esta situación, esperamos que los ejemplos de esta página puedan ayudarte.

Ejemplo 1:el problema

Primero, aquí hay un ejemplo del problema real. Tenga en cuenta que podemos recuperar el SET DATEFIRST valor seleccionando @@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';

Resultado:

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

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

En este caso, la primera fecha cae en domingo y la segunda fecha en lunes. Por lo tanto, normalmente esperaría que el británico DATEDIFF() resultado para devolver 1 . Esperaría esto porque el límite de la parte de la semana se cruza cuando va de domingo a lunes (porque SET DATEFIRST el valor es 1 que significa “lunes”, y el lunes marca el comienzo de una nueva semana).

Pero debido a que DATEDIFF() ignora su SET DATEFIRST valor y asume que el domingo es el comienzo de la semana, obtenemos el mismo resultado para ambos idiomas.

Solo para estar seguro, volveré a ejecutar la consulta, pero esta vez configuraré SET DATEFIRST valor explícitamente . En otras palabras, en lugar de configurar el idioma, usaré SET DATEFIRST declaración:

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

Resultado:

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

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

Mismo resultado, incluso cuando configura explícitamente SET DATEFIRST valor. Sin embargo, esto no es una sorpresa; me sorprendería si no devolver el mismo resultado.

Además, esto simplemente confirma que DATEDIFF() está funcionando exactamente como se esperaba.

Entonces, ¿cómo lo cambiamos para que nuestro DATEDIFF() los resultados respetan nuestro SET DATEFIRST valor?

La solución

Aquí hay una solución que le permitirá obtener los resultados deseados. Esto asegurará que su SET DATEFIRST la configuración se tiene en cuenta en su DATEDIFF() resultados.

Todo lo que necesitas hacer es restar @@DATEFIRST a partir de las fechas de entrada.

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

Resultado:

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

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

Esto usa el DATEADD() función para reducir las fechas de entrada por la cantidad de @@DATEFIRST (que es su SET DATEFIRST valor).

En este caso el DATEDIFF() La función todavía usa el domingo como el primer día de la semana, sin embargo, las fechas reales usadas en el cálculo son diferentes. Han retrocedido en el tiempo por la cantidad de @@DATEFIRST .

El siguiente ejemplo muestra las fechas que se usaron en el cálculo:

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

Resultado:

+-----------------+---------------+------------------+
| 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       |
+-----------------+---------------+------------------+

Entonces, en nuestra solución alternativa, DATEDIFF() usó la "Fecha resultante" en sus cálculos.

Si ha tenido problemas con DATEDIFF() ignorando SET DATEFIRST , espero que este artículo haya ayudado.