Sqlserver
 sql >> Base de Dados >  >> RDS >> Sqlserver

Uma solução alternativa para DATEDIFF() ignorando SET DATEFIRST no SQL Server (Exemplo de T-SQL)


Uma coisa interessante sobre o DATEDIFF() função no SQL Server é que ele ignora seu SET DATEFIRST valor.

No entanto, isso não é um bug. Documentação da Microsoft para DATEDIFF() afirma claramente o seguinte:

Especificando SET DATEFIRST não tem efeito em DATEDIFF . DATEDIFF sempre usa o domingo como o primeiro dia da semana para garantir que a função funcione de forma determinística.

Caso você não saiba, SET DATEFIRST define o primeiro dia da semana para sua sessão. É um número de 1 a 7 (que corresponde de segunda a domingo).

O valor inicial para SET DATEFIRST édefinido implicitamente pela configuração do idioma (que poderádefinir com o SET LANGUAGE demonstração). O valor real dependerá do idioma definido. Por exemplo, o valor padrão para o us_english o idioma é 7 (Domingo), enquanto o padrão para o British idioma é 1 (Segunda-feira).

No entanto, você pode usar um SET DATEFIRST para substituir isso para que você possa continuar usando o mesmo idioma enquanto usa um dia diferente para o primeiro dia da semana.

Mas como mencionado, o SET DATEFIRST valor não tem efeito no DATEDIFF() função. O DATEDIFF() função sempre assume que domingo é o primeiro dia da semana independentemente do seu SET DATEFIRST valor.

Isso pode causar alguns problemas interessantes ao usar DATEDIFF() se você não sabe como funciona.

Se você se encontra nessa situação, espero que os exemplos nesta página possam ajudar.


Exemplo 1 – O problema


Primeiro, aqui está um exemplo do problema real. Observe que podemos recuperar o SET DATEFIRST valor selecionando @@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 Valor | us_english DATEDIFF() Resultado ||----------------------------------+------------------- -------------|| 7 | 0 |+-----------------------+----------------------- ---------++------------+--------------- --------------+| SET DATEFIRST Valor | Resultado britânico DATEDIFF() ||----------------------------------+------------------- ----------|| 1 | 0 |+-----------------------+----------------------- ------+

Nesse caso, a primeira data cai em um domingo e a segunda data em uma segunda-feira. Portanto, você normalmente esperaria que o DATEDIFF() britânico resultado para retornar 1 . Você esperaria isso porque o limite da parte da semana é cruzado quando vai de domingo a segunda (porque o SET DATEFIRST o valor é 1 que significa “segunda-feira”, e segunda-feira marca o início de uma nova semana).

Mas porque DATEDIFF() ignora seu SET DATEFIRST valor e assume que domingo é o início da semana, obtemos o mesmo resultado para ambos os idiomas.

Só para ter certeza, executarei a consulta novamente, mas desta vez definirei o SET DATEFIRST valor explicitamente . Em outras palavras, em vez de definir o idioma, usarei o SET DATEFIRST demonstração:
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 Valor | us_english DATEDIFF() Resultado ||----------------------------------+------------------- -------------|| 7 | 0 |+-----------------------+----------------------- ---------++------------+--------------- --------------+| SET DATEFIRST Valor | Resultado britânico DATEDIFF() ||----------------------------------+------------------- ----------|| 1 | 0 |+-----------------------+----------------------- ------+

Mesmo resultado, mesmo quando você define explicitamente o SET DATEFIRST valor. No entanto, isso não é surpresa – eu ficaria surpreso se não retornar o mesmo resultado.

Além disso, isso simplesmente confirma que DATEDIFF() está funcionando exatamente como pretendido.

Então, como podemos alterá-lo para que nosso DATEDIFF() os resultados honram nosso SET DATEFIRST valor?

A solução


Aqui está uma solução/solução que permitirá que você obtenha os resultados pretendidos. Isso garantirá que seu SET DATEFIRST configurações são fatoradas em seu DATEDIFF() resultados.

Tudo que você precisa fazer é subtrair @@DATEFIRST das datas 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 Valor', DATEADD(semana, DATEADD(dia, [email protected]@DATEFIRST, @datainicial), DATEADD(dia, [email protected]@DATEFIRST, @enddate)) AS 'Resultado britânico DATEDIFF()'; 
Resultado:
+-----------------------+---------------------- ----------+| SET DATEFIRST Valor | us_english DATEDIFF() Resultado ||----------------------------------+------------------- -------------|| 7 | 0 |+-----------------------+----------------------- ---------++------------+--------------- --------------+| SET DATEFIRST Valor | Resultado britânico DATEDIFF() ||----------------------------------+------------------- ----------|| 1 | 1 |+-----------------------+----------------------- ------+

Isso usa o DATEADD() função para reduzir as datas de entrada pela quantidade de @@DATEFIRST (que é o seu SET DATEFIRST valor).

Neste caso, o DATEDIFF() A função ainda usa domingo como o primeiro dia da semana, no entanto, as datas reais usadas no cálculo são diferentes. Eles foram movidos de volta no tempo pela quantidade de @@DATEFIRST .

O exemplo a seguir mostra as datas que foram usadas no 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(dia, [email protected]@DATEFIRST, @startdate) AS 'Data Resultante'UNION ALLSELECT @enddate, @@DATEFIRST, DATEADD(dia, [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 ALLSELECT @enddate, @@DATEFIRST , DATEADD(dia, [email protected]@DATEFIRST, @enddate); 

Resultado:
+-----------------+---------------+------------ ------+| Data Original | Subtrair por | Data resultante ||-----------------+---------------+---------------- ------|| 2025-01-05 | 7 | 29-12-2024 || 2025-01-06 | 7 | 30/12/2024 |+-----------------+---------------+--------- ---------++------+---------------+----- -------------+| Data Original | Subtrair por | Data resultante ||-----------------+---------------+---------------- ------|| 2025-01-05 | 1 | 04-01-2025 || 2025-01-06 | 1 | 2025-01-05 |+-----------------+---------------+--------- ---------+

Portanto, em nossa solução alternativa, DATEDIFF() utilizou a “Data Resultante” em seus cálculos.

Se você está tendo problemas com DATEDIFF() ignorando SET DATEFIRST , espero que este artigo tenha ajudado.