Olá pessoal,
Boa noite! Tudo bem ?
Neste post vou comentar sobre um recurso utilizado por muitos desenvolvedores para “resolver” rapidamente alguns problemas comuns no desenvolvimento de queries Transact-SQL. Esse recurso é o SET ANSI_WARNINGS OFF.
Para que serve o comando SET ANSI_WARNINGS OFF?
A opção de controle de sessão ANSI_WARNINGS altera o comportamento do motor do SQL Server para algumas condições, que são:
- Truncamento de strings (Tentar armazenar um texto de 10 caracteres num VARCHAR(9))
- Divisão por ZERO
- Valores NULL em funções de agregação (MAX, SUM, COUNT, etc)
O valor padrão é o ON, que irá apresentar o comportamento padrão ISO para esse tipo de situação, fazendo com que mensagens de erro sejam geradas nas duas primeiras situações e um alerta seja gerado na última situação.
Se você utilizar o comando SET ANSI_WARNINGS OFF, o engine do SQL Server vai reproduzir um comportamento fora do padrão e tentar executar o comando SELECT 10/0, ao invés de uma mensagem de erro, vai retornar NULL e exibir apenas um alerta, permitindo que suas rotinas retornem informações erradas e dados incorretos.
Por este motivo, alguns desenvolvedores SQL, querendo evitar que a aplicação retorne erro nessas situações acabam optando pelo modo mais fácil de “resolver” erros como os listados acima ao invés de realmente atuar no problema e resolvê-lo efetivamente. Isso geralmente ocorre nas empresas devido a diversos motivos, os quais destaco:
- Falta de conhecimento técnico para identificar e corrigir o erro
- Falta de vontade de efetivamente resolver o problema
- Prazos muito apertados para entrega do software funcionando e sem erros
Msg 8134 Divide by zero error encountered
Erro comum no dia a dia dos desenvolvedores SQL, a clássica mensagem de divisão por zero é um indício que algum valor nos seus cálculos está errado. Esse é um erro aritmético básico e que causa uma exceção grave no SQL Server, fazendo com que o comando seja interrompido e a transação sofra um rollback automático. Esse é o comportamento padrão da engine do SQL Server, seguindo o padrão ISO.
O controle desse comportamento é feito em conjunto com o comando ARITHABORT. Quando o ANSI_WARNIGS está ativado, o ARITHABORT é ativado automaticamente, fazendo com que a engine funcione em seu modo padrão (ISO).
Quando o ANSI_WARNINGS está desativado, o ARITHABORT pode ser alterado para mudar o comportamento padrão do Engine. Se ARITHABORT está desativado, ao encontrar um erro de divisão por zero, o SQL Server irá ignorar o erro e continuar a execução normalmente, apenas exibindo um alerta na aba Messages.
Exemplo:
1 2 3 4 5 6 7 8 9 |
-- Ativa o ARITHABORT automaticamente SET ANSI_WARNINGS ON -- Mesmo desativando manualmente, ficará ATIVADO por causa do ANSI_WARNINGS ON SET ARITHABORT OFF IF ((@@OPTIONS & 8) > 0) PRINT 'SET ANSI_WARNINGS is ON' SELECT 1/0 |
Como vimos no exemplo acima, o ANSI_WARNINGS OFF permite que contas de divisão por zero sejam executadas silenciosamente, gerando dados incorretos e sem que a equipe de desenvolvimento fique sabendo, uma vez que não será gerada nenhuma exceção na aplicação.
Retorno com SET ANSI_WARNINGS ON (Padrão):
Retorno com SET ANSI_WARNINGS OFF:
Msg 8152 String or binary data would be truncated
Essa mensagem de erro é muito comum entre os desenvolvedores SQL e com certeza vocês já devem ter visto enquanto criam suas queries, fazem integrações entre sistemas, etc. Essa mensagem ocorre quando você tenta armazenar uma quantidade de caracteres maior que o permitido em uma coluna.
O comportamento padrão do motor do SQL Server, seguindo o padrão ANSI, faz com que seja gerado uma exceção na execução do seu código Transact-SQL caso você
tenha um texto de 10 caracteres e tente inserir esse texto em uma coluna que permite só até 9 caracteres.
Quando você utiliza o comando SET ANSI_WARNINGS OFF, você faz com que o motor do SQL Server não gere mais esse erro na execução, fazendo com que o seu texto de 10 caracteres seja truncado e armazenado na coluna de 9 caracteres. Os caracteres excedentes serão silenciosamente descartados, ignorando e mascarando um problema na gravação dos dados do seu sistema, sem que ninguém fique sabendo.
Exemplo:
1 2 3 4 5 6 7 8 9 10 11 12 |
SET NOCOUNT ON SET ANSI_WARNINGS OFF IF ((@@OPTIONS & 8) > 0) PRINT 'SET ANSI_WARNINGS is ON' IF (OBJECT_ID('tempdb..#Teste') IS NOT NULL) DROP TABLE #Teste CREATE TABLE #Teste ( Nome VARCHAR(10) ) INSERT INTO #Teste VALUES ('Dirceu Resende') -- 14 caracteres SELECT * FROM #Teste |
Retorno com SET ANSI_WARNINGS ON (Padrão):
Retorno com SET ANSI_WARNINGS OFF:
Null value is eliminated by an aggregate or other SET operation
Mensagem de alerta que ocorre quando é aplicada uma função de agregação (MAX, SUM, COUNT, AVG, etc) em um conjunto de dados e ao menos 1 registro possui um valor nulo (NULL).
Exemplo:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
SET NOCOUNT ON SET ANSI_WARNINGS ON IF ((@@OPTIONS & 8) > 0) PRINT 'SET ANSI_WARNINGS is ON' SELECT MAX(Nota) FROM ( SELECT 7 AS Nota UNION SELECT 6.5 UNION SELECT 5.9 UNION SELECT 4.2 UNION SELECT NULL UNION SELECT 8.7 ) AS Aluno |
Retorno com SET ANSI_WARNINGS ON (Padrão):
Retorno com SET ANSI_WARNINGS OFF
Reparem que neste caso, se a opção ANSI_WARNINGS estiver desativada, você pode estar ignorando a existência de valores nulos no seu conjunto de resultados, o que podem indicar um possível problemas, uma vez que em determinadas situações, isso pode representar algum erro na sua query e que você está mascarando.
Caso você queira saber como remover o warning “Null value is eliminated by an aggregate or other SET operation” da forma correta, saiba mais acessando o post SQL Server – Warning: Null value is eliminated by an aggregate or other SET operation.
INSERT failed because the following SET options have incorrect settings: ‘ANSI_WARNINGS’
Mais um grave efeito colateral do uso do ANSI_WARNINGS OFF é o impacto que esse comando influi sobre views indexadas e índices criados em colunas calculadas, que muitas vezes são desenhados para ganho de performance, mas que a simples criação de um índice com o intuito de melhorar, acaba criando uma grande dor de cabeça para o DBA, uma vez que todas as rotinas que inserem dados em tabelas que possuem colunas calculados irão falhar após a criação do índice na coluna calculada.
Diferente dos erros anteriores, que podem causar dados incorretos e inconsistentes, esse exemplo impede que as rotinas funcionem de um modo geral, inclusive já acontecendo comigo, que criei um índice para otimizar uma query lenta na produção e começou a disparar alertas de erros em um dos módulos do sistema de onde trabalho.
Exemplo:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
SET ANSI_WARNINGS ON IF ((@@OPTIONS & 8) > 0) PRINT 'SET ANSI_WARNINGS is ON' IF (OBJECT_ID('tempdb..#Teste') IS NOT NULL) DROP TABLE #Teste CREATE TABLE #Teste ( Nota1 NUMERIC(5, 2) NOT NULL, Nota2 NUMERIC(5, 2) NOT NULL, Media AS ((Nota1 + Nota2) / 2) ) CREATE NONCLUSTERED INDEX SK01_Teste ON #Teste(Media) INSERT INTO #Teste VALUES (7.6, 8.9) SELECT * FROM #Teste |
Retorno com SET ANSI_WARNINGS ON (Padrão):
Retorno com SET ANSI_WARNINGS OFF:
Msg 1934, Level 16, State 1, Line 12
CREATE INDEX failed because the following SET options have incorrect settings: ‘ANSI_WARNINGS’. Verify that SET options are correct for use with indexed views and/or indexes on computed columns and/or filtered indexes and/or query notifications and/or XML data type methods and/or spatial index operations.
É isso aí, pessoal!
Espero que tenham gostado do post e até a próxima.