Uma consulta pode contar zero clientes sem e-mail quando existem vários, um filtro pode deixar vendas de fora sem motivo aparente, e uma conta pode resultar em vazio. Esses comportamentos costumam ter a mesma origem: o NULL, a marca que o banco usa para indicar que uma coluna não tem valor. O NULL segue regras próprias, diferentes das regras dos valores comuns.
O que é o NULL
O NULL indica que a coluna não tem valor naquela linha. O NULL não é o número zero, nem um texto vazio, nem um espaço. No cenário, a tabela vendas tem dois exemplos:
- a coluna
cliente_idfica em NULL quando o consumidor não se identifica na compra; - a coluna
descontofica em NULL quando a venda não tem desconto.
A definição da tabela informa se cada coluna aceita o NULL. Uma coluna definida como NOT NULL exige um valor em todas as linhas. O comando SHOW COLUMNS FROM vendas; mostra essa informação na coluna Null.
Comparações com NULL
Uma comparação com NULL não resulta em verdadeiro nem em falso, e sim em desconhecido, porque não é possível saber se um valor ausente é igual ou diferente de outro. O filtro de uma consulta só mantém as linhas em que a condição é verdadeira, e por isso as linhas com resultado desconhecido ficam de fora. Para testar a ausência de valor, o SQL tem operadores próprios:
SELECT * FROM clientes WHERE email IS NULL;: encontra os clientes sem e-mail;SELECT * FROM clientes WHERE email IS NOT NULL;: encontra os clientes com algum e-mail;SELECT * FROM clientes WHERE email = NULL;: não encontra nenhuma linha, porque a comparação com o sinal de igual nunca é verdadeira quando envolve NULL.
O mesmo vale para a comparação de diferença. Na tabela vendas, a coluna situacao guarda a letra F para as vendas finalizadas e a letra C para as canceladas. Se algumas linhas antigas têm essa coluna em NULL, a condição a seguir as deixa de fora:
SELECT * FROM vendas WHERE situacao <> 'C';: traz as vendas com situação diferente de C, mas não traz as vendas com a situação em NULL.
Para incluir essas linhas, a condição precisa citá-las:
SELECT * FROM vendas WHERE situacao <> 'C' OR situacao IS NULL;
NULL e texto vazio
Um texto vazio, escrito como duas aspas simples sem nada entre elas, é um valor. Um NULL é a ausência de valor. Uma mesma coluna pode ter os dois casos, dependendo de como cada tela grava os dados. Para encontrar todos os clientes sem e-mail, a consulta precisa considerar as duas formas:
SELECT * FROM clientes WHERE email IS NULL OR email = '';
Contagens, somas e contas
As funções que resumem dados tratam o NULL de formas diferentes:
COUNT(*)conta as linhas, com ou sem NULL;COUNT(desconto)conta apenas as linhas em que a coluna tem valor;SUM(desconto)eAVG(desconto)ignoram as linhas em NULL, e a média é calculada apenas sobre as linhas com valor.
Por isso, em uma tabela com 100 vendas, das quais 30 têm desconto, COUNT(*) resulta em 100, e COUNT(desconto) resulta em 30.
Em uma conta, qualquer operação com NULL resulta em NULL. A expressão total - desconto resulta em NULL nas vendas sem desconto. A função COALESCE troca o NULL por um valor escolhido:
SELECT total - COALESCE(desconto, 0) FROM vendas;: usa zero no lugar do desconto ausente.
A função CONCAT, que une textos, segue a mesma regra. O resultado dessa função é NULL quando uma das partes é NULL, e a ferramenta mostra NULL no lugar do texto esperado.
Uma armadilha com NOT IN
A condição NOT IN compara um valor com uma lista. Quando a lista contém um NULL, a condição nunca resulta em verdadeiro, e a consulta não devolve nenhuma linha. Um exemplo é a busca pelos clientes que nunca compraram:
SELECT * FROM clientes WHERE id NOT IN (SELECT cliente_id FROM vendas);: não devolve nenhuma linha se alguma venda tivercliente_idem NULL;SELECT * FROM clientes WHERE id NOT IN (SELECT cliente_id FROM vendas WHERE cliente_id IS NOT NULL);: retira o NULL da lista e devolve os clientes sem compras.
O NULL na ordenação
Na ordem crescente, o MySQL coloca as linhas com NULL antes de todas as outras. Na ordem decrescente, essas linhas vão para o fim. Uma lista de clientes ordenada por e-mail começa, portanto, pelos clientes sem e-mail.
No suporte
Quando uma contagem ou um filtro deixa linhas de fora sem explicação, convém verificar se a coluna tem valores NULL. A consulta SELECT COUNT(*) FROM vendas WHERE situacao IS NULL mostra quantas linhas estão nessa situação, e o resultado indica se a condição precisa tratar o NULL.
Para guardar
- O NULL indica a ausência de valor e não é zero nem texto vazio.
- Comparações com NULL resultam em desconhecido, e o filtro descarta essas linhas.
- IS NULL e IS NOT NULL são as formas de testar a ausência de valor.
- COUNT(*) conta as linhas, e COUNT(coluna) conta apenas os valores presentes.
- Contas com NULL resultam em NULL, e COALESCE troca o NULL por um valor.
- Um NULL na lista do NOT IN faz a consulta não devolver nenhuma linha.
A consulta SELECT COUNT(*) FROM clientes WHERE email = NULL resulta em zero, mas existem clientes sem e-mail. Por quê?
Porque a comparação com o sinal de igual nunca é verdadeira quando envolve NULL, e as linhas ficam de fora do filtro. A condição correta é email IS NULL. Se o sistema também grava e-mails vazios, a condição precisa incluir email = ''.
A consulta com a condição situacao <> 'C' deveria trazer todas as vendas não canceladas, mas deixa algumas de fora. Qual é a explicação provável?
As vendas que estão com a coluna situacao em NULL. A comparação de NULL com qualquer valor resulta em desconhecido, e a linha não entra no resultado. Se essas vendas devem aparecer, a condição precisa acrescentar OR situacao IS NULL.