No SQL, NULL nao significa "vazio" nem "zero", mas "valor desconhecido". Esse detalhe faz com que comparacoes comuns se comportem de forma inesperada, e e exatamente ai que IS DISTINCT FROM se mostra util. Vamos ver por que = NULL nunca e verdadeiro e como escrever comparacoes que sobrevivem ao NULL.
Logica de tres valores e por que = NULL falha
A maioria dos motores SQL usa logica de tres valores: uma expressao pode ser TRUE, FALSE ou UNKNOWN. Qualquer comparacao que envolva NULL resulta em UNKNOWN, e as linhas com UNKNOWN em um WHERE sao simplesmente descartadas.
SELECT NULL = NULL;
SELECT NULL <> 1;
SELECT NULL = 1;
Por isso este filtro sempre retorna zero linhas, mesmo parecendo correto:
SELECT id, email
FROM users
WHERE country = NULL;
Regras principais:
NULL = NULL e UNKNOWN, nao TRUE.
- O
WHERE mantem apenas linhas que avaliam para TRUE.
- Aritmetica com
NULL tambem da NULL: amount + NULL e NULL.
IS NULL e IS NOT NULL
Para testar especificamente a "ausencia de valor", use os predicados dedicados IS NULL e IS NOT NULL. Eles sempre retornam TRUE ou FALSE, nunca o terceiro estado.
SELECT id, email
FROM users
WHERE country IS NULL;
SELECT id, email
FROM users
WHERE country IS NOT NULL;
Atencao com NOT IN contra uma subconsulta: se um unico valor da lista for NULL, todo o NOT IN colapsa para UNKNOWN e voce nao recebe nenhuma linha.
SELECT id, name
FROM employees
WHERE id NOT IN (SELECT manager_id FROM employees);
E mais seguro usar NOT EXISTS, ou filtrar explicitamente os NULL dentro da subconsulta.
IS DISTINCT FROM compara dois valores como se NULL fosse um valor comum. NULL IS DISTINCT FROM NULL e FALSE (eles "nao diferem"), enquanto NULL IS DISTINCT FROM 1 e TRUE. O operador espelho, IS NOT DISTINCT FROM, e a igualdade segura com NULL.
SELECT NULL IS DISTINCT FROM NULL;
SELECT NULL IS DISTINCT FROM 1;
SELECT 1 IS NOT DISTINCT FROM 1;
SELECT NULL IS NOT DISTINCT FROM NULL;
Compare o comportamento de = e de IS NOT DISTINCT FROM lado a lado:
a = b: TRUE para nao-NULL iguais, FALSE caso contrario, mas UNKNOWN sempre que ha NULL.
a IS NOT DISTINCT FROM b: comporta-se como =, mas trata NULL como igual a NULL e sempre retorna TRUE/FALSE.
Na pratica: deteccao de mudancas e deduplicacao
Uma tarefa classica: durante uma carga, atualizar uma linha apenas quando algo realmente mudou. Com um <> comum, uma linha cujo name passou de NULL para um valor real (ou o contrario) nao e sinalizada como alterada.
SELECT u.id
FROM users AS u
JOIN staging_users AS s ON s.id = u.id
WHERE u.name IS DISTINCT FROM s.name
OR u.country IS DISTINCT FROM s.country
OR u.email IS DISTINCT FROM s.email;
O mesmo vale para a deduplicacao quando a chave pode conter NULL:
SELECT a.id, b.id
FROM users AS a
JOIN users AS b
ON a.email IS NOT DISTINCT FROM b.email
AND a.country IS NOT DISTINCT FROM b.country
WHERE a.id < b.id;
Diferencas entre motores:
- O PostgreSQL suporta totalmente
IS DISTINCT FROM e IS NOT DISTINCT FROM.
- MySQL/MariaDB usam no lugar o operador
<=> (igualdade segura com NULL): a <=> b.
- O ClickHouse oferece
isNull e similares; para comparacao ciente de NULL e comum uma expressao baseada em coalesce, e o suporte exato varia conforme a versao.
Resumindo: use IS NULL/IS NOT NULL para testar presenca, e recorra a IS DISTINCT FROM sempre que comparar colunas onde NULL e um estado legitimo.
No SQL,
NULLnao significa "vazio" nem "zero", mas "valor desconhecido". Esse detalhe faz com que comparacoes comuns se comportem de forma inesperada, e e exatamente ai queIS DISTINCT FROMse mostra util. Vamos ver por que= NULLnunca e verdadeiro e como escrever comparacoes que sobrevivem aoNULL.Logica de tres valores e por que = NULL falha
A maioria dos motores SQL usa logica de tres valores: uma expressao pode ser
TRUE,FALSEouUNKNOWN. Qualquer comparacao que envolvaNULLresulta emUNKNOWN, e as linhas comUNKNOWNem umWHEREsao simplesmente descartadas.SELECT NULL = NULL; -- NULL (not TRUE) SELECT NULL <> 1; -- NULL SELECT NULL = 1; -- NULLPor isso este filtro sempre retorna zero linhas, mesmo parecendo correto:
-- Wrong: never returns rows, even if country really is NULL SELECT id, email FROM users WHERE country = NULL;Regras principais:
NULL = NULLeUNKNOWN, naoTRUE.WHEREmantem apenas linhas que avaliam paraTRUE.NULLtambem daNULL:amount + NULLeNULL.IS NULL e IS NOT NULL
Para testar especificamente a "ausencia de valor", use os predicados dedicados
IS NULLeIS NOT NULL. Eles sempre retornamTRUEouFALSE, nunca o terceiro estado.-- Correct way to find users without a country SELECT id, email FROM users WHERE country IS NULL; -- Users that have a country set SELECT id, email FROM users WHERE country IS NOT NULL;Atencao com
NOT INcontra uma subconsulta: se um unico valor da lista forNULL, todo oNOT INcolapsa paraUNKNOWNe voce nao recebe nenhuma linha.-- Gotcha: if any manager_id is NULL, this returns nothing SELECT id, name FROM employees WHERE id NOT IN (SELECT manager_id FROM employees);E mais seguro usar
NOT EXISTS, ou filtrar explicitamente osNULLdentro da subconsulta.IS DISTINCT FROM: igualdade segura com NULL
IS DISTINCT FROMcompara dois valores como seNULLfosse um valor comum.NULL IS DISTINCT FROM NULLeFALSE(eles "nao diferem"), enquantoNULL IS DISTINCT FROM 1eTRUE. O operador espelho,IS NOT DISTINCT FROM, e a igualdade segura com NULL.SELECT NULL IS DISTINCT FROM NULL; -- false SELECT NULL IS DISTINCT FROM 1; -- true SELECT 1 IS NOT DISTINCT FROM 1; -- true SELECT NULL IS NOT DISTINCT FROM NULL; -- trueCompare o comportamento de
=e deIS NOT DISTINCT FROMlado a lado:a = b:TRUEpara nao-NULL iguais,FALSEcaso contrario, masUNKNOWNsempre que haNULL.a IS NOT DISTINCT FROM b: comporta-se como=, mas trataNULLcomo igual aNULLe sempre retornaTRUE/FALSE.Na pratica: deteccao de mudancas e deduplicacao
Uma tarefa classica: durante uma carga, atualizar uma linha apenas quando algo realmente mudou. Com um
<>comum, uma linha cujonamepassou deNULLpara um valor real (ou o contrario) nao e sinalizada como alterada.-- Detect real changes, NULL-safe SELECT u.id FROM users AS u JOIN staging_users AS s ON s.id = u.id WHERE u.name IS DISTINCT FROM s.name OR u.country IS DISTINCT FROM s.country OR u.email IS DISTINCT FROM s.email;O mesmo vale para a deduplicacao quando a chave pode conter
NULL:-- Two rows are duplicates even when country is NULL in both SELECT a.id, b.id FROM users AS a JOIN users AS b ON a.email IS NOT DISTINCT FROM b.email AND a.country IS NOT DISTINCT FROM b.country WHERE a.id < b.id;Diferencas entre motores:
IS DISTINCT FROMeIS NOT DISTINCT FROM.<=>(igualdade segura com NULL):a <=> b.isNulle similares; para comparacao ciente deNULLe comum uma expressao baseada emcoalesce, e o suporte exato varia conforme a versao.Resumindo: use
IS NULL/IS NOT NULLpara testar presenca, e recorra aIS DISTINCT FROMsempre que comparar colunas ondeNULLe um estado legitimo.