Então, nossa equipe de dados pediu ajuda para resolver um problema que eles tinham. Acabei rastreando alguns dados realmente fora de alcance (1/1/0001) e uma função DATEDIFF que eles estavam usando. Enquanto eu resolvi o problema deles, aconteceu que eu realmente não sei o que o 0 se transforma quando usado como eles o usavam.
Eu originalmente pensei que estava mais perto de um estouro de número inteiro em vez de um verdadeiro erro de conversão, mas não é isso. Eu tentei em uma caixa SQL 2016 com DATEDIFF_BIG e mesmo erro. Eu tenho uma amostra para vocês abaixo para brincarem com o que funciona e o que não funciona.
/** Setup The Sample */
DECLARE @TestValue DATETIME2(7)
SET @TestValue = '0001-01-01 10:30:00.0000000'
/** Conversion Error
Msg 242, Level 16, State 3, Line 10
The conversion of a datetime2 data type to a datetime data type resulted in an out-of-range value.
*/
SELECT DATEDIFF(MINUTE, 0, @TestValue)
--Also does not work, same error.
SELECT DATEDIFF_BIG(MINUTE, 0, @TestValue)
/** Works */
SELECT DATEDIFF(MINUTE, '1/1/1900', @TestValue)
/** Works */
SELECT DATEDIFF(MINUTE, CAST(0 AS DATETIME), @TestValue)
/** Doesn't Work, you can't cast 0 to a DATETIME2 */
--SELECT DATEDIFF(MINUTE, CAST(0 AS DATETIME2), @TestValue)
/** Works (or no error, which is fine)*/
SELECT DATEDIFF(MINUTE, 0, TRY_CAST(@TestValue AS DATETIME))
Pergunta bônus, já que 0 não funciona em todos os casos para DATETIME2, qual é a alternativa?
O QUE DECIDIMOS FAZER
Então, comecei a recomendar que minha equipe faça o seguinte, já que você vê 0 em muitos exemplos de datemath (primeiro dia do mês etc.). Portanto, recomendo que você faça uma conversão explícita de 0 para datetime e continue como desejar. Isso evitará o erro enquanto ainda estiver funcionando. Então:
DATEDIFF(MINUTE, CAST(DATETIME, 0), <Date>)
Você pode ver o que está acontecendo adicionando a expressão a uma consulta com uma
FROM
cláusula e observando o escalar de computação.Isso mostra o seguinte.
Se você passar um literal
0
para esta função, ele sempre será convertido implicitamente emdatetime
.Lançar um
int
paradatetime
retorna1900-01-01 + <int> days
assim1900-01-01
.O problema para você é para que tipo de dados o terceiro parâmetro é convertido.
@TestValue
é dedatetime2
- quando você passa um inteiro, ambos os lados são convertidos implicitamente paradatetime
.'0001-01-01 10:30:00.0000000'
está fora do intervalo paradatetime
, portanto, o erro.Nos momentos em que é bem-sucedido, os parâmetros são lançados para
datetimeoffset(7)