Descobri um problema ao inserir dados em nosso banco de dados. Minha instrução de inserção estava verificando a existência de dados na cláusula WHERE para evitar a inserção de dados duplicados. Nenhum foi detectado e o INSERT aconteceu. No entanto, a restrição exclusiva rejeitou os dados porque já existiam no banco de dados.
O problema era que os dados a serem inseridos eram DATETIMEOFFSET(2) e o campo do banco de dados inserido era DATETIME.
Para mostrar que você quer que eu esteja falando, execute o seguinte:
DECLARE @dt DATETIME = '2014-07-07 09:49:33.000';
DECLARE @dto DATETIMEOFFSET(2) = '2014-07-07 09:49:33.00 +07:00';
PRINT CASE WHEN @dt = @dto THEN 'Equals matches'
ELSE 'Equals does not match'
END
PRINT CASE WHEN @dt = CAST(@dto AS DATETIME) THEN 'Cast matches'
ELSE 'Cast does not match'
END
Ele imprime:
- Igual não corresponde
- Transmitir partidas
O operador de comparação (=) não funciona da mesma forma que a conversão implícita se você inserir os dados. Na verdade, o operador cast/convert descarta o deslocamento! Loucura.
Por que o operador de comparação funciona de maneira diferente da conversão implícita que ocorre durante um INSERT?
Parece que o oposto é verdadeiro: a conversão implícita leva o deslocamento para a equação, mas as funções cast/convert não.
A comparação (deduzidas 7 horas de @dt) resulta em:
Fiz mais algumas investigações e me deparei com este artigo.
"Quando você converte de datetime2 ou datetimeoffset para date, não há arredondamento e a parte da data é extraída explicitamente. Para qualquer conversão implícita de datetimeoffset para data, hora, datetime2, datetime ou smalldatetime, a conversão é baseada na data e hora locais valor."
Então, quando você quiser tratar '2014-07-07 09:49:33.000' e '2014-07-07 09:49:33.000 +07:00' como iguais, sua única opção é fazer uma conversão explícita via cast ou converter. Como as conversões implícitas só funcionariam quando o deslocamento do fuso horário do seu servidor fosse o mesmo que o deslocamento especificado em @dto.
Bem, a comparação e o CAST funcionam de maneira diferente.
A comparação usa precedência de tipo para determinar como comparar tipos diferentes.
DATETIMEOFFSET
está em 4º lugar,DATETIME
eDATETIME2
em 6º e 5º lugar respectivamente. Portanto, oDATETIME
argumento é convertido (usandoCAST
, veja abaixo) paraDATETIMEOFFSET
primeiro.A conversão para
DATETIMEOFFSET
pode ser alcançada de três maneiras possíveis (talvez mais?):CAST
que adiciona fuso horário "+00:00" (UTC)CONVERT
que adiciona fuso horário "+00:00" (UTC)AT TIMEZONE
que adiciona qualquer fuso horário que você fornecer como o argumento do lado direitoInfelizmente, nunca encontrei nenhuma documentação sobre o comportamento de
CAST
nemCONVERT
quando "upscaling" paraDATETIMEOFFSET
. Portanto, esse comportamento pode diferir entre máquinas e versões.Por outro lado, a conversão para
DATETIME
corta a parte "deslocada".Então, no seu caso: