NULL: quando o valor não existe
O que você vai aprender
- entender que
NULLnão é zero, nem string vazia, nem um «valor ruim», e sim a ausência de valor - verificar valores ausentes com
IS NULLeIS NOT NULL - explicar por que
x = NULL,x <> NULLe atéNULL = NULLnão funcionam como parece - entender a lógica de três valores do SQL:
TRUE,FALSEeUNKNOWN - prever quais linhas passam pelo
WHEREe quais são descartadas - colocar valores de reserva na saída com
COALESCE, sem alterar os dados da tabela - evitar os erros mais comuns em entrevistas e em consultas de verdade
NULL é quando não existe valor
Nos hologramas dos arquivos antigos aparecem falhas: uma célula não acende e, através dela, vê-se a escuridão.
No começo parece um erro. Será que esqueceram de exibir o valor? Será que ali deveria haver um zero? Ou uma string vazia?
Mas o Arquivo é honesto. Onde ele não sabe a resposta, não inventa uma.
NULL em SQL significa: não existe valor.
Não é «o valor é igual a zero».
Não é «ali tem um texto vazio».
Não é «ali houve um erro».
É exatamente isto: o valor está ausente ou é desconhecido.
Veja, por exemplo, a tabela users:
SELECT id, name, city
FROM users;
Resultado:
| id | name | city |
|---|---|---|
| 1 | Anna | Moscou |
| 2 | Boris | |
| 3 | Vera | Kazan |
| 4 | Gleb |
À primeira vista, as linhas 2 e 4 se parecem: Boris e Gleb aparentemente não têm cidade. Mas, para o SQL, são situações diferentes.
Boris tem NULL em city — a cidade é desconhecida ou não foi informada.
Gleb pode ter uma string vazia '' em city — e isso já é um valor, apenas um texto com 0 caractere.
Essa diferença é importante.
NULL -- não existe valor
'' -- existe valor, mas é um texto vazio
0 -- existe valor, e é o número zero
NULL não é um valor. É uma marca especial: o valor está ausente.
QUERY: Respeite os buracos na memória. Um banco que diz honestamente «não sei» é mais confiável do que um que mente com convicção.
NULL, zero e string vazia são coisas diferentes
Quem está começando costuma enxergar o NULL como “vazio” e depois se espanta quando as consultas se comportam de um jeito estranho.
Vamos ver isso num exemplo simples.
Digamos que a tabela products tenha uma coluna discount_percent:
| id | title | discount_percent |
|---|---|---|
| 1 | Ração “Atum Lunar” | 10 |
| 2 | Tigela antigravitacional | 0 |
| 3 | Caminha do arquivista |
O que significa cada linha?
O primeiro item tem 10% de desconto.
O segundo item tem 0% de desconto. Ou seja: o desconto é conhecido e vale zero.
No terceiro item está um NULL. Ou seja: não sabemos o desconto — ele não foi informado, não foi calculado ou ainda não foi carregado.
Ou seja:
discount_percent = 0
e
discount_percent IS NULL
— são estados diferentes.
O mesmo vale para texto.
city = ''
significa: a cidade está registrada como uma string vazia.
city IS NULL
significa: não há cidade nenhuma naquela célula.
Em bancos reais isso importa. Uma string vazia pode vir de um formulário em que a pessoa apagou o texto e enviou o campo em branco. Já um NULL pode significar que o campo nunca chegou a ser preenchido, que os dados ainda não vieram de outro serviço ou que o valor é mesmo desconhecido.
NULL. Não é string vazia nem zero, mas ausência de valor.Por que = NULL não funciona
Agora, a maior pegadinha.
Parece que, para encontrar os usuários sem cidade, bastaria escrever assim:
SELECT id, name, city
FROM users
WHERE city = NULL;
Mas uma consulta dessas não encontra as linhas com NULL.
O motivo é que NULL não é um valor comum. Ele não pode ser comparado com = do mesmo jeito que um número ou um texto.
O SQL raciocina mais ou menos assim:
city = 'Москва'
Isso dá para verificar. Se city guarda 'Москва', a resposta é TRUE. Se guarda 'Казань', a resposta é FALSE.
Já isto:
city = NULL
O SQL não consegue responder TRUE nem FALSE, porque NULL significa “o valor é desconhecido”.
Se a cidade é desconhecida, dá para dizer que ela é igual a NULL? Não.
Dá para dizer que ela é diferente de NULL? Também não.
A resposta do SQL é: .
Ou seja: “desconhecido”.
No SQL não existem só dois resultados lógicos:
TRUE
FALSE
Existe um terceiro:
UNKNOWN
Isso se chama lógica de três valores.
NULL usando = ou <> produz a terceira, e o WHERE não deixa essa linha passar.NULL se comportam de um jeito especial. Os operadores comuns = e <> não dão nem TRUE nem FALSE: o resultado que aparece na saída é NULL, ou seja, o valor lógico UNKNOWN.O WHERE só deixa passar TRUE
Agora um passo importante.
O WHERE mantém a linha só quando a condição devolveu TRUE.
Se a condição devolveu FALSE, a linha é descartada.
Se a condição devolveu UNKNOWN, a linha também é descartada.
Ou seja, para o WHERE vale esta regra:
| Resultado da condição | A linha entra no resultado? |
|---|---|
| TRUE | sim |
| FALSE | não |
| não |
É exatamente por isso que a consulta:
SELECT id, name, city
FROM users
WHERE city = NULL;
não devolve os usuários sem cidade.
Para uma linha em que city é NULL, a expressão:
city = NULL
não dá TRUE, e sim UNKNOWN.
E o WHERE só deixa passar TRUE.
Como testar NULL do jeito certo
Para o NULL existem verificações próprias:
coluna IS NULL
e
coluna IS NOT NULL
Se você precisa encontrar os usuários que não têm cidade informada:
SELECT id, name, city
FROM users
WHERE city IS NULL;
Se precisa encontrar os usuários que têm cidade informada:
SELECT id, name, city
FROM users
WHERE city IS NOT NULL;
Guarde a regra principal:
-- errado
WHERE city = NULL
-- errado
WHERE city <> NULL
-- certo
WHERE city IS NULL
-- certo
WHERE city IS NOT NULL
O IS NULL não compara o valor com NULL. Ele faz outra pergunta:
Está faltando valor nesta célula?
E o IS NOT NULL pergunta:
Esta célula tem valor?
IS NULL e IS NOT NULL são o jeito certo de filtrar linhas pela ausência ou pela presença de valor.Por que o <> também pode surpreender
Outro erro frequente aparece com o operador <>.
Digamos que você precise encontrar todos os usuários que não são de Moscou.
Quem está começando escreve:
SELECT id, name, city
FROM users
WHERE city <> 'Москва';
Parece que a consulta deveria devolver:
- os usuários de Kazan
- os usuários de outras cidades
- os usuários que não têm cidade informada
Mas as linhas com NULL não entram no resultado.
Por quê?
Para a linha com a cidade 'Казань':
city <> 'Москва'
dá TRUE.
Para a linha com a cidade 'Москва':
city <> 'Москва'
dá FALSE.
Para a linha com NULL:
city <> 'Москва'
dá UNKNOWN.
E o WHERE só deixa passar TRUE.
Por isso a linha com a cidade desconhecida é descartada.
Para incluir os usuários que não têm cidade informada, é preciso acrescentar a condição de forma explícita:
SELECT id, name, city
FROM users
WHERE city <> 'Москва'
OR city IS NULL;
Agora a lógica é esta:
Mostre todos os que não são de Moscou e também aqueles cuja cidade não foi informada.
No PostgreSQL existe ainda um operador prático:
WHERE city IS DISTINCT FROM 'Москва'
Ele lida com o NULL de forma mais previsível e devolve estritamente TRUE ou FALSE.
Por exemplo:
NULL IS DISTINCT FROM 'Москва'
devolve TRUE.
E:
NULL IS NOT DISTINCT FROM NULL
devolve TRUE.
Mas, para quem está começando, a regra básica continua a mesma: quando você precisa lidar com um valor ausente, use IS NULL e IS NOT NULL.
<> não inclui as linhas com NULL, porque comparar com um valor desconhecido dá UNKNOWN.Cuidado com NOT, AND e OR
É nas condições compostas que o NULL mais quebra expectativas.
Olhe esta condição:
WHERE NOT (city = 'Москва')
À primeira vista, ela parece igual a:
WHERE city <> 'Москва'
E, de fato, para valores comuns o resultado é parecido.
Mas, se city for NULL, a expressão:
city = 'Москва'
dá UNKNOWN.
E agora o ponto importante:
NOT UNKNOWN
também dá UNKNOWN.
Não TRUE.
Por isso a linha com NULL continua fora do resultado.
Exemplo:
SELECT id, name, city
FROM users
WHERE NOT (city = 'Москва');
As linhas com cidade desconhecida não passam.
Para incluí-las, é preciso escrever de forma explícita:
SELECT id, name, city
FROM users
WHERE city <> 'Москва'
OR city IS NULL;
Vale guardar esta regra:
O
NOTnão transformaUNKNOWNemTRUE.
No SQL, UNKNOWN continua sendo desconhecido mesmo depois da negação.
COALESCE: uma luz de reserva para os buracos
Às vezes um NULL nos dados é uma situação perfeitamente normal, mas mostrá-lo assim num relatório não ajuda quem lê.
Por exemplo:
SELECT id, name, city
FROM users;
Resultado:
| id | name | city |
|---|---|---|
| 1 | Anna | Moscou |
| 2 | Boris |
Dentro do banco, o NULL é claro. Mas, numa interface ou num relatório, é melhor mostrar à pessoa um rótulo legível:
cidade não informada
Para isso usamos o COALESCE.
O COALESCE devolve o primeiro argumento que não for NULL.
SELECT COALESCE(NULL, NULL, 'valor de reserva');
Resultado:
| coalesce |
|---|
| valor de reserva |
Vamos aplicá-lo à tabela:
SELECT
id,
name,
COALESCE(city, 'cidade não informada') AS city_for_report
FROM users;
Resultado:
| id | name | city_for_report |
|---|---|---|
| 1 | Anna | Moscou |
| 2 | Boris | cidade não informada |
| 3 | Vera | Kazan |
Importante: o COALESCE não muda os dados da tabela.
Ele muda apenas a saída da consulta.
Ou seja, na tabela, o Boris continua com um NULL. É só no resultado da consulta que mostramos um texto compreensível no lugar dele.
É como a plaquinha numa vitrine vazia: não apareceu produto nenhum, apareceu uma explicação para quem olha.
COALESCE ajuda a trocar o NULL por um valor de reserva no resultado da consulta, sem alterar os dados originais.O COALESCE pode escolher entre várias opções
O COALESCE não aceita só dois argumentos, e sim quantos você quiser.
Ele vai da esquerda para a direita e devolve o primeiro argumento que não for NULL.
Por exemplo, na tabela de usuários pode haver várias formas de contato:
| id | name | telegram | phone | |
|---|---|---|---|---|
| 1 | Anna | @anna | anna@mail.test | |
| 2 | Boris | NULL | boris@mail.test | NULL |
| 3 | Vera | NULL | NULL | +7001 |
Queremos mostrar o melhor contato disponível: primeiro o Telegram, se houver; se não, o email; se não, o telefone; e, se não houver nada, o texto “sem contato”.
SELECT
id,
name,
COALESCE(telegram, email, phone, 'sem contato') AS best_contact
FROM users;
Resultado:
| id | name | best_contact |
|---|---|---|
| 1 | Anna | @anna |
| 2 | Boris | boris@mail.test |
| 3 | Vera | +7001 |
A lógica é esta:
pegue telegram
se telegram for NULL — pegue email
se email for NULL — pegue phone
se phone for NULL — pegue 'sem contato'
Esse é um padrão muito comum em relatórios, exportações e interfaces.
Um detalhe técnico importante: os argumentos do COALESCE precisam ter tipos compatíveis.
Assim funciona:
COALESCE(city, 'cidade não informada')
porque as duas opções são texto.
Já assim pode dar erro:
COALESCE(discount_percent, 'sem desconto')
Se discount_percent for um número e 'sem desconto' for texto, o banco pode não entender que tipo de resultado você espera.
Nesses casos, o normal é converter o número para texto explicitamente antes:
COALESCE(discount_percent::text, 'sem desconto')
NULL no SELECT e NULL no WHERE são histórias diferentes
É importante distinguir duas situações.
No SELECT, você pode ver o NULL no resultado:
SELECT id, name, city
FROM users;
Aqui o NULL só aparece como o valor de uma célula do resultado. Cada editor de SQL pode mostrá-lo de um jeito: como NULL, como uma célula vazia ou como uma marca especial.
Já no WHERE o NULL influencia a filtragem:
SELECT id, name, city
FROM users
WHERE city <> 'Москва';
Aqui a linha com NULL pode sumir do resultado, porque a condição virou UNKNOWN.
Ou seja: o SELECT mostra os dados, e o WHERE decide se a linha passa ou não.
Para o WHERE, o que importa não é como a célula fica bonita na tela, e sim se a condição devolveu estritamente TRUE.
Erros comuns com NULL
Erro 1. Procurar NULL com =
WHERE city = NULL
Não escreva assim. Essa condição não encontra as linhas com NULL.
Certo:
WHERE city IS NULL
Erro 2. Procurar valores preenchidos com <> NULL
WHERE city <> NULL
Também não escreva assim. Essa condição não encontra as linhas preenchidas.
Certo:
WHERE city IS NOT NULL
Erro 3. Achar que o <> inclui NULL
WHERE city <> 'Москва'
Essa consulta encontra as cidades que com certeza não são Moscou. Mas ela não devolve as linhas em que a cidade é desconhecida.
Se você precisa tanto de “não é Moscou” quanto de “cidade não informada”:
WHERE city <> 'Москва'
OR city IS NULL
Erro 4. Confundir string vazia com NULL
WHERE city = ''
Isso procura a string vazia, não o NULL.
Para encontrar as duas situações:
WHERE city = ''
OR city IS NULL
Com dados sujos, às vezes é preciso verificar as duas coisas.
Erro 5. Usar COALESCE e achar que os dados mudaram
SELECT COALESCE(city, 'cidade não informada') AS city
FROM users;
Essa consulta muda apenas a exibição do resultado.
Ela não grava 'cidade não informada' na tabela.
Para mudar os dados de verdade é preciso um UPDATE, mas isso já é outra operação e outra responsabilidade.
A regra principal desta aula
NULL não pode ser testado com comparações comuns.
Escreva assim:
WHERE column IS NULL
ou assim:
WHERE column IS NOT NULL
Não escreva assim:
WHERE column = NULL
nem assim:
WHERE column <> NULL
O WHERE só deixa passar as linhas em que a condição devolveu TRUE.
FALSE e UNKNOWN não chegam ao resultado.
Interview question
Pergunta de entrevista:
Por que WHERE city = NULL não encontra as linhas em que a cidade não está preenchida?
Resposta forte:
NULL não é um valor comum, e sim a ausência de valor. Por isso a comparação city = NULL não retorna TRUE, mesmo quando city realmente contém NULL. O resultado é UNKNOWN. E o WHERE só deixa passar as linhas em que a condição deu TRUE. Logo, para testar um valor ausente é preciso escrever city IS NULL.
Pergunta de entrevista:
Por que WHERE city <> 'Москва' não retorna as linhas em que a cidade não está preenchida?
Resposta forte:
Se city for NULL, a expressão city <> 'Москва' dá UNKNOWN, e não TRUE. O WHERE só deixa passar TRUE, então as linhas com NULL ficam de fora. Para incluí-las no resultado, escreva:
WHERE city <> 'Москва'
OR city IS NULL
No PostgreSQL, também dá para usar:
WHERE city IS DISTINCT FROM 'Москва'
Esse operador trata NULL como um estado comparável à parte e retorna estritamente TRUE ou FALSE.
Pergunta de entrevista:
Em que NULL difere do zero e da string vazia?
Resposta forte:
Zero e string vazia são valores. O zero é o valor numérico 0; a string vazia é um texto com 0 caractere. Eles são iguais a si mesmos e participam das comparações comuns. NULL significa que o valor não existe ou é desconhecido. Por isso comparações com NULL usando =, <>, <, > dão UNKNOWN. Para testar NULL usamos IS NULL e IS NOT NULL.
Pergunta de entrevista:
O que NULL = NULL retorna?
Resposta forte:
Não retorna TRUE, e sim UNKNOWN. Em SQL, NULL significa valor desconhecido. Dois valores desconhecidos não podem ser considerados iguais só porque ambos são desconhecidos. Por isso, para testar a ausência de valor não se usa = NULL, e sim IS NULL.
Pergunta de entrevista:
O que o COALESCE faz?
Resposta forte:
O COALESCE retorna o primeiro argumento que não for NULL. Ele é muito usado no SELECT para colocar um valor de reserva num relatório ou numa interface. Por exemplo:
COALESCE(city, 'cidade não informada')
Se city estiver preenchida, volta a cidade. Se city for NULL, volta o texto 'cidade não informada'. E os dados da tabela continuam intactos.
NULL = NULL devolve?WHERE deixa passar?COALESCE(city, 'cidade não informada') faz?QUERY: O vazio também fala. O importante é não obrigá-lo a se passar por um valor.
- Drop Unrated Reviews, Replace Empty CommentsEASY
- Quem indicou cada clienteEASY
- Peças não concluídas: versão alternativaEASY