sqlpostgresqlnullthree-valued-logic

NULL e IS DISTINCT FROM: comparacao segura com NULL no SQL

Por que = NULL nunca e verdadeiro e como IS DISTINCT FROM oferece igualdade segura com NULL.

2 min de leituraReferencesql · postgresql · null · three-valued-logic · data-quality

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;      -- NULL (not TRUE)
SELECT NULL <> 1;        -- NULL
SELECT NULL = 1;         -- NULL

Por 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 = 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.

-- 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 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.

-- 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 os NULL dentro da subconsulta.

IS DISTINCT FROM: igualdade segura com NULL

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;      -- false
SELECT NULL IS DISTINCT FROM 1;         -- true
SELECT 1 IS NOT DISTINCT FROM 1;        -- true
SELECT NULL IS NOT DISTINCT FROM NULL;  -- true

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.

-- 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:

  • 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.

Pratique com exercícios reais

Resolva exercícios no treinador de SQL com correção instantânea e dicas.

Abrir o treinador