Hey Guys!
Tudo jóia ?
Depois de quase 15 dias sem postar em conta de vários trabalhos em que estava atuando, consegui um tempinho pra fazer esse post rápido pra vocês sobre um erro que encontrei ao somar uma coluna DATETIME com uma do tipo TIME. Esse tipo de operação ocorria normalmente quando o modo de compatibilidade do banco estava em 100 (SQL), mas ao alterar o modo de compatibilidade para 110 (SQL 2012), 120 (SQL 2014) ou superiores, alguns jobs que eu administro começaram a apresentar a seguinte mensagem de erro:
Msg 402, Level 16, State 1, Line 1
The data types datetime and time are incompatible in the add operator.
Simulando o erro
Existem várias formas de simular, como criando uma tabela com uma coluna do tipo DATETIME e outra do tipo TIME, mas acho que o teste fica mais prático se criarmos o select manualmente, como no script abaixo:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
USE [Testes] GO PRINT 'Horário atual: ' + CONVERT(VARCHAR(19), GETDATE()) ALTER DATABASE Testes SET COMPATIBILITY_LEVEL = 100 -- SQL 2008 GO PRINT GETDATE() + CAST('00:12:13' AS TIME) GO ALTER DATABASE Testes SET COMPATIBILITY_LEVEL = 110 -- SQL 2012 GO PRINT GETDATE() + CAST('00:12:13' AS TIME) GO ALTER DATABASE Testes SET COMPATIBILITY_LEVEL = 120 -- SQL 2014 GO PRINT GETDATE() + CAST('00:12:13' AS TIME) GO |
Porque esse erro ocorre ?
Esse erro passou a ocorrer porque a Microsoft realizou melhorias na Engine dos databases a partir do SQL Server 2012, e a ideia de realizar operações entre variáveis de tipos de dados diferentes sem uma conversão explÃcita (deixando que o banco faça a conversão implÃcita) nunca é uma boa ideia.
Um exemplo clássico de que essa prática não deve ser utilizada pode ser visto ao trabalhar com dados do tipo INT e FLOAT sem uma conversão explÃcita:
Pela definição universal da matemática, a forma como o SQL Server realiza essa conta simples está ERRADA. Isso ocorre porque na divisão de 1 por 2, o SQL Server faz a divisão considerando que os 2 números são inteiros, e por isso, o resultado também deve ser inteiro, fazendo com que 1/2, que é 0.5, seja retornado como 0 e depois somado 1.
Sempre garanta que os tipos de dados envolvidos são compatÃveis com as operações realizadas:
Fiz esse mesmo testes no Oracle Database e no MySQL e os dois bancos conseguiram realizar a conta corretamente, pois esses bancos já fazem a conversão implÃcita de inteiro para float em casos de divisão e multiplicação, coisa que o SQL Server não faz. Por este motivo, sempre recomendo que o desenvolvedor da Query se encarregue em pensar neste tipo de situação, e não deixe que o banco “pense” por você.
Já tive experiências pessoais e vi o quão desastroso podem ser relatórios gerenciais que tem esse tipo de falha entre INT e FLOAT, principalmente em taxas e indicadores percentuais.
Dito isso, a Microsoft vem tentando reduzir esse tipo de equÃvoco e não permite mais operações envolvendo DATETIME e TIME utilizando operadores simples (+) e (-), apenas com conversões como CAST e CONVERT, onde você pode (e deveria) definir o estilo da conversão que será realizada, evitando a perda de precisão dos dados quando convertidos.
Como contornar essa limitação e resolver o problema?
Pois bem, já encontrei diversas soluções mirabolantes na internet, vários tratamentos de strings e outras coisas não muito performáticas, quando se tem uma solução tão simples e boba para resolver isso, que é converter a coluna TIME para DATETIME:
And that's it, folks!
Obrigado pela visita e até o próximo post.
Muito obrigado.
Mudando a compatibilidade aqui e tinha uma procedure que deu esse problema.