Valores SQL NULL explicados: IS NULL, COALESCE, NULLIF e as armadilhas a serem evitadas
NULL causa alguns dos bugs mais silenciosos do SQL, com consultas que são executadas sem erros, mas perdem linhas ou distorcem as médias. Aprenda como o NULL realmente se comporta, com exemplos testados, e como lidar com isso com segurança.
- NULL significa um valor ausente ou desconhecido. Não é zero e não é uma string vazia.
- Teste com
IS NULL, porque= NULLnunca corresponde a nada. - A maioria dos cálculos com NULL fornece NULL e
WHEREmantém apenas linhas que são definitivamente verdadeiras. - Os agregados ignoram NULLs, o que pode alterar silenciosamente uma média.
Uma consulta é executada sem erros e retorna um número aparentemente sensato, mas faltam linhas e a média está errada. Valores SQL NULL são muitas vezes a causa. NULL não se comporta como nenhum valor normal e isso surpreende tanto iniciantes quanto desenvolvedores experientes.
Este guia mostra exatamente como o NULL se comporta. Executamos todas as consultas no SQLite 3.50 e os resultados mostrados vêm dessa execução. A maior parte desse comportamento é SQL padrão e observamos onde os bancos de dados diferem, como a ordem de classificação NULL e o tratamento de strings vazias pelo Oracle.
O que é NULO?
NULL é um marcador que significa que um valor está faltando ou é desconhecido. Não é um valor no sentido usual, então são coisas diferentes:
| Valor | Significado | Exemplo para uma coluna de telefone |
|---|---|---|
NULO |
Desconhecido ou não fornecido | Não sabemos o número |
'' |
Um valor de texto vazio e conhecido | Enviado em branco |
0 |
O número zero | Não é relevante para o texto, mas é real para valores |
A diferença é importante especialmente para os valores. Um bônus de 0 significa que a pessoa não recebeu nada. Um bônus NULL significa que ninguém sabe ainda.
A tabela de amostra
CREATE TABLE employees (
id INT, name TEXT, phone TEXT, bonus INT, manager_id INT
);
INSERT INTO employees VALUES
(1,'Meera','555-0101',500,NULL),
(2,'Arjun',NULL,300,1),
(3,'Sara','',NULL,1),
(4,'Ravi',NULL,NULL,2);
Observe que Sara possui uma string vazia para o telefone, enquanto Arjun e Ravi possuem NULL. Os dois são tratados de forma muito diferente.
Testando para NULL
Aqui está o erro que quase todo mundo comete no início:
SELECT name FROM employees WHERE phone = NULL;
-- (no rows)
SELECT name FROM employees WHERE phone IS NULL;
-- Arjun
-- Ravi
SELECT name FROM employees WHERE phone IS NOT NULL;
-- Meera
-- Sara
A primeira consulta é SQL válida e não apresenta erros. Simplesmente não retorna nada, porque comparar qualquer coisa com NULL nunca é verdade. Sara aparece em IS NOT NULL, pois uma string vazia é um valor real. No Oracle, porém, uma string vazia é tratada como NULL, o que é uma exceção notável.
Lógica de três valores
Na maioria das linguagens de programação, uma condição é verdadeira ou falsa. SQL adiciona um terceiro resultado: desconhecido. Se você não conhece um valor, não pode dizer se ele é igual a 5. E dois valores desconhecidos não são iguais, e é por isso que NULL = NULL não é verdadeiro. Em nosso teste, todos retornaram NULL:
SELECT NULL = NULL, NULL <> 1, 1 + NULL, 'a' || NULL;
-- NULL | NULL | NULL | NULL
A combinação de condições segue regras cuidadosas. Aqui estão os resultados da nossa execução, onde 1 é verdadeiro e 0 é falso:
| Expressão | Resultado | Por que |
|---|---|---|
VERDADEIRO E NULO |
NULO | Depende da parte desconhecida |
FALSO E NULO |
Falso | Falso seja qual for o desconhecido |
VERDADEIRO OU NULO |
Verdadeiro | Verdadeiro seja qual for o desconhecido |
FALSO OU NULO |
NULO | Depende da parte desconhecida |
NÃO NULO |
NULO | O oposto de desconhecido é desconhecido |
Linhas que desaparecem de WHERE
WHERE mantém uma linha somente se sua condição for verdadeira. As linhas onde o resultado é desconhecido são eliminadas, juntamente com aquelas que são falsas. Isso leva a resultados surpreendentes:
SELECT name FROM employees WHERE phone <> '555-0101';
-- Sara
Você pode esperar que Arjun e Ravi apareçam, já que o telefone deles certamente não é esse número. Mas o telefone deles é desconhecido, então o resultado é desconhecido e eles desaparecem. Para incluí-los, diga explicitamente: WHERE phone <> '555-0101' OR phone IS NULL. A negação também não ajuda:
SELECT name FROM employees WHERE NOT (bonus > 400);
-- Arjun
Sara e Ravi não têm bônus, então não aparecem nem em bonus > 400 nem no oposto. Isto é algo importante a lembrar: uma condição e sua negação nem sempre cobrem todas as linhas.
COALESCE: fornece um substituto
COALESCE retorna o primeiro valor não NULL em sua lista e funciona em todos os principais bancos de dados:
SELECT COALESCE(NULL, NULL, 'third', 'fourth');
-- third
SELECT name, COALESCE(phone, 'no phone') FROM employees;
-- Meera | 555-0101
-- Arjun | no phone
-- Sara |
-- Ravi | no phone
Sara ainda aparece em branco porque uma string vazia não é NULL. Existem funções semelhantes, como IFNULL no MySQL e SQLite, ISNULL no SQL Server e NVL no Oracle. COALESCE é a escolha portátil. Nosso ponto anterior sobre cálculos também se aplica aqui. bonus + 100 dá NULL para qualquer pessoa sem bônus, então escreva COALESCE(bonus, 0) + 100 se tratar um bônus faltante como zero for correto para o seu negócio.
NULLIF: transforma um valor em NULL
NULLIF(a, b) retorna NULL se os dois valores forem iguais e, caso contrário, retorna a. Dois usos são especialmente úteis. Ele pode limpar valores de espaço reservado:
SELECT name, NULLIF(phone, '') FROM employees WHERE id = 3;
-- Sara | NULL
SELECT name FROM employees
WHERE COALESCE(NULLIF(phone, ''), '') = '';
-- Arjun
-- Sara
-- Ravi
Essa segunda consulta é uma maneira prática de encontrar valores “ausentes ou em branco”. Também pode proteger contra a divisão por zero, o que gera um erro em muitos bancos de dados:
SELECT total / NULLIF(quantity, 0) FROM sales;
Quando quantity é 0, isso fornece NULL em vez de falhar. Em nosso teste, SELECT 100 / NULLIF(0, 0) retornou NULL.
Agregados ignoram NULL
É aqui que NULL causa mais danos aos relatórios:
SELECT COUNT(*), COUNT(bonus), SUM(bonus), AVG(bonus),
AVG(COALESCE(bonus, 0))
FROM employees;
-- 4 | 2 | 800 | 400.0 | 200.0
| Expressão | Resultado | O que isso significa |
|---|---|---|
COUNT(*) |
4 | Todas as linhas |
COUNT(bonus) |
2 | Somente linhas com bônus |
SUM(bonus) |
800 | NULLs são ignorados |
AVG(bonus) |
400 | 800 dividido por 2, não por 4 |
AVG(COALESCE(bônus, 0)) |
200 | 800 dividido por 4 |
Então, qual é o bônus médio, 400 ou 200? Depende do que NULL significa nos seus dados. Se NULL significa “nenhum bônus foi pago”, então 200 está certo. Se significar “ainda não decidido”, então 400 pode estar certo. O banco de dados não pode tomar essa decisão por você, então a pergunta deve ir para alguém que conheça o negócio.
Uma armadilha relacionada: SUM sem valores é NULL em vez de 0. Quando somamos apenas as linhas sem bônus, obtemos NULL, enquanto COUNT retornou 0. O código do aplicativo que espera um número pode falhar nisso, então envolva-o: COALESCE(SUM(bonus), 0).
Dica: quando o total do relatório parecer errado, compare COUNT(*) com COUNT(column). Qualquer lacuna entre eles informa imediatamente quantos NULLs aquela coluna possui.
GROUP BY, classificação e DISTINCT
Embora NULLs “não sejam iguais” nas comparações, essas operações os tratam como um grupo:
SELECT bonus, COUNT(*) FROM employees GROUP BY bonus ORDER BY bonus;
-- NULL | 2
-- 300 | 1
-- 500 | 1
SELECT DISTINCT bonus FROM employees;
-- 500, 300, NULL
Consulte SQL GROUP BY e HAVING para obter mais informações sobre agrupamento. A ordem de classificação é onde os bancos de dados realmente diferem. SQLite e MySQL tratam NULL como o menor valor, enquanto PostgreSQL e Oracle o tratam como o maior. Então o mesmo ORDER BY bonus DESC pode colocar NULLs por último em um e primeiro em outro. PostgreSQL, Oracle e SQLite moderno permitem afirmar isso diretamente, usando NULLS FIRST ou NULLS LAST.
Junta-se
Uma condição de junção usando = nunca corresponde a NULL, então linhas com uma chave NULL são eliminadas de uma junção interna:
SELECT e.name FROM employees e
JOIN employees m ON m.id = e.manager_id;
-- Arjun, Sara, Ravi (Meera is gone)
SELECT e.name, m.name FROM employees e
LEFT JOIN employees m ON m.id = e.manager_id;
-- Meera | NULL
-- Arjun | Meera
-- Sara | Meera
-- Ravi | Arjun
Meera não tem gerente, portanto, uma junção interna elimina silenciosamente essa linha. Um LEFT JOIN o mantém e também cria seus próprios NULLs para o lado ausente. Leia mais em Explicação sobre junções SQL.
A armadilha NÃO ESTÁ
Um único NULL em uma lista NOT IN elimina todos os resultados:
SELECT name FROM employees WHERE manager_id NOT IN (1);
-- Ravi
SELECT name FROM employees WHERE manager_id NOT IN (1, NULL);
-- (no rows)
A segunda forma significa manager_id <> 1 AND manager_id <> NULL, e a última parte nunca pode ser verdadeira. Isto é particularmente perigoso com subconsultas, onde um NULL pode chegar despercebido. Use NOT EXISTS, conforme explicado em nosso tutorial de subconsultas SQL. Meera também está faltando no primeiro resultado, já que manager_id dessa linha é NULL.
Projetando com NULL
- Use
NOT NULLpor padrão. Permita NULL somente quando um valor realmente estiver faltando. - Adicione padrões razoáveis. Por exemplo,
quantity INT NOT NULL DEFAULT 0. - Não use valores mágicos. Uma data de 1900-01-01 ou uma idade de -1 substituindo “desconhecido” corromperá seus cálculos.
- Conheça o comportamento UNIQUE. A maioria dos bancos de dados permite vários NULLs em uma coluna
UNIQUE, e nosso teste inseriu dois sem erro. O SQL Server é uma exceção, pois normalmente permite. - Lembre-se de que uma chave primária não pode ser NULA. Consulte chave primária versus chave estrangeira.
- Teste com NULLs. Dados de teste sem NULLs ocultam esses bugs. Inclua-os propositalmente, como faria com outros casos extremos.
Considerações finais
Trate NULL como “desconhecido” e a maior parte de seu comportamento começa a fazer sentido: desconhecido mais um é desconhecido e desconhecido é igual a desconhecido não é conhecido como verdadeiro. Audite um de seus próprios relatórios esta semana. Verifique as colunas anuláveis usando COUNT(*) em relação a COUNT(column), procure qualquer NOT IN e decida o que NULL deve significar em cada média.
Perguntas frequentes
O que é NULO em SQL?
NULL é um marcador para um valor ausente ou desconhecido. É diferente de zero e de uma string vazia e precisa de tratamento especial nas comparações.
Por que =NULL não funciona?
Comparar qualquer coisa com NULL resulta em desconhecido em vez de verdadeiro, então WHERE descarta a linha. Use IS NULL ou IS NOT NULL para testar.
Qual é a diferença entre COALESCE e ISNULL?
COALESCE é SQL padrão e aceita muitos argumentos. ISNULL é específico do SQL Server e leva dois. Para código portátil, prefira COALESCE.
COUNT inclui valores NULL?
COUNT(*) conta todas as linhas, incluindo aquelas com NULLs. COUNT(coluna) conta apenas as linhas onde essa coluna não é NULL, o que pode fornecer um número menor.
NULL é o mesmo que uma string vazia?
Não, na maioria dos bancos de dados. Uma string vazia é um valor conhecido sem caracteres e NULL significa desconhecido. Oracle é uma exceção notável, pois trata strings vazias como NULL.
Continue aprendendo
Precisa de ajuda com testes, WordPress ou SEO?
Ajudamos as equipes a fornecer software melhor e a expandir seus sites. Conte-nos no que você está trabalhando e sugeriremos um próximo passo claro.
Mais guias sobre testes, IA e desenvolvimento moderno
Gostou deste artigo? Escolha um tópico e vá direto para nossos tutoriais mais recentes.
Explicação das alucinações de IA: por que os modelos de linguagem inventam as coisas e como reduzi-las
Explicação das alucinações de IA: o que são, por que os modelos de linguagem as produzem, tipos comuns, como reduzi-las com aterramento e instruções…
Leia o artigo →DevOpsVariáveis de ambiente explicadas: arquivos .env, segredos e configuração bem feitos
Variáveis de ambiente explicadas com exemplos de Python que executamos: leitura e configuração, por que cada valor é uma string, arquivos .env, hera…
Leia o artigo →Desenvolvimento de softwareExplicação da limitação de taxa de API: algoritmos, cabeçalhos e tratamento de erros 429
Limitação de taxa de API explicada com uma simulação que executamos: token bucket, janela fixa, janela deslizante, 429 respostas, cabeçalhos de limi…
Leia o artigo →Automação de testesZombando em Python: unittest.mock, patch e os erros que fazem os testes mentirem
Zombaria em Python com unittest.mock, testado: Mock e return_value, side_effect, patch onde um nome é usado, especificação e autospec, zombaria de d…
Leia o artigo →Teste de softwareCobertura de código explicada: cobertura de linha versus filial e o que 100% realmente significa
Cobertura de código explicada com uma execução cobertura.py real: cobertura de linha, ramal e condição, leitura de um relatório, por que 100% de cob…
Leia o artigo →Bancos de dados e SQLDELETE vs TRUNCATE vs DROP em SQL: diferenças, reversão e quando usar cada um
DELETE vs TRUNCATE vs DROP explicado com SQL testado: o que cada um remove, rollback, aciona, redefinição de identidade, velocidade e como PostgreSQ…
Leia o artigo →Bancos de dados e SQLExibições SQL explicadas: crie, use e evite as armadilhas, com exemplos
Visualizações SQL explicadas com consultas que executamos: criação de uma visualização, resultados ao vivo, ocultação de colunas, desempenho, visual…
Leia o artigo →Desenvolvimento de softwareInterface TypeScript vs tipo: diferenças reais, testadas com o compilador
Interface TypeScript versus tipo, verificada com o compilador real: o que ambos podem fazer, o que apenas um pode fazer, mesclagem de declarações, e…
Leia o artigo →Desenvolvimento de softwarePython *args e **kwargs explicados: funções flexíveis com exemplos claros
Python *args e **kwargs explicados com o código que executamos: empacotamento e descompactação, ordem dos parâmetros, argumentos somente de palavras…
Leia o artigo →Desenvolvimento de softwareFunções Python Lambda: sintaxe, usos reais e quando não usá-los
Funções lambda do Python explicadas com o código que executamos: sintaxe, classificação com chave, mapa e filtro, fechamentos, armadilha de ligação …
Leia o artigo →Inteligência artificialO que são agentes de IA? Como eles funcionam, com um loop de agente simples que você pode executar
O que são agentes de IA? Um guia simples para o loop do agente, ferramentas, memória e proteções, com um pequeno loop Python que executamos, riscos …
Leia o artigo →DevOpsMonitoramento versus observabilidade: a verdadeira diferença, com logs, métricas e rastreamentos
Explicação do monitoramento versus observabilidade: falhas conhecidas e desconhecidas, logs, métricas e rastreamentos, um passo a passo do incidente…
Leia o artigo →DevOpsDocker Volumes vs Bind Mounts: persista os dados do contêiner da maneira certa
Volumes Docker versus montagens de ligação explicadas: por que os dados do contêiner desaparecem, volumes nomeados, montagens de ligação, tmpfs, exe…
Leia o artigo →Automação de testesTDD vs BDD: principais diferenças, um recurso criado de ambas as maneiras e quando usar cada um
TDD vs BDD explicado com um recurso de desconto construído em ambos os sentidos: refatoração vermelho-verde com o código que executamos, cenários da…
Leia o artigo →Teste de softwareTeste de aceitação do usuário (UAT): processo, lista de verificação e exemplos reais
Explicação do teste de aceitação do usuário: quem o executa, como ele difere do controle de qualidade, um processo de sete etapas, um modelo de caso…
Leia o artigo →Bancos de dados e SQLValores SQL NULL explicados: IS NULL, COALESCE, NULLIF e as armadilhas a serem evitadas
Valores SQL NULL explicados com consultas que executamos: lógica de três valores, IS NULL, COALESCE, NULLIF, como agregações, junções e classificaçã…
Leia o artigo →Bancos de dados e SQLInstrução SQL CASE WHEN: sintaxe, exemplos reais e erros comuns
SQL CASE WHEN explicado com consultas que executamos: formulários simples e pesquisados, ordem de condição, pivôs com SUM, truques COUNT, classifica…
Leia o artigo →Desenvolvimento de softwareO que é JWT? JSON Web Tokens explicados com um exemplo prático
O que é JWT? JSON Web Tokens explicados: as três partes, um token construído e adulterado no código que executamos, JWT vs sessões, armazenamento, t…
Leia o artigo →Desenvolvimento de softwareLoop de eventos JavaScript explicado: pilha de chamadas, microtarefas e temporizadores com exemplos
O loop de eventos JavaScript explicado com o código que executamos: pilha de chamadas, filas de tarefas e microtarefas, por que setTimeout 0 não é i…
Leia o artigo →Desenvolvimento de softwareGeradores Python e rendimento explicado: economize memória com exemplos reais
Geradores Python e rendimento explicados com o código que executamos: como eles pausam e reiniciam, medem economia de memória, pipelines, sequências…
Leia o artigo →Inteligência artificialO que são incorporações? Similaridade de vetores e cossenos explicada de forma simples
O que são incorporações? Um guia em linguagem simples para vetores, similaridade de cossenos e pesquisa semântica, com um pequeno exemplo que execut…
Leia o artigo →DevOpsImplantação azul-verde vs canário: diferenças, compensações e como escolher
Explicação da implantação azul-verde versus canário: como cada um funciona, custo, risco e reversão, agrupamento estável de usuários que testamos, m…
Leia o artigo →Desenvolvimento de softwareGit merge vs rebase: diferenças, exemplos e quando usar cada um
Git merge vs rebase explicado com saída real do log, uma tabela comparativa, a regra de ouro do rebase e um fluxo de trabalho seguro para equipes.
Leia o artigo →Desenvolvimento de softwareList comprehension em Python: sintaxe, exemplos e quando evitar
List comprehension em Python explicada: sintaxe, filtros, if-else, laços aninhados, tempos medidos e quando um laço for comum é a melhor escolha.
Leia o artigo →Bancos de dados e SQLSQL JOIN explicado com exemplos (Inner, Left, Right, Full)
Entenda os joins SQL passo a passo com tabelas e consultas de exemplo: INNER, LEFT, RIGHT, FULL, CROSS e SELF, além de erros comuns e dicas de desem…
Leia o artigo →