SQL EXISTS: filtre linhas por registros relacionados

SQL EXISTS retorna verdadeiro quando uma subconsulta correlacionada encontra pelo menos uma linha correspondente. Use-o para filtrar linhas pai pela presença (ou ausência) de linhas filhas relacionadas, sem duplicar resultados como um JOIN pode fazer. Otimizadores de consulta costumam executar EXISTS como um semi-join, então podem encerrar o trabalho da subconsulta após a primeira correspondência.

Exemplo de SQL EXISTS para filtrar existência de linhas

Saída:

A saída aparecerá aqui...

Saída:

+------+
| nome |
+------+
| Alma |
| Cara |
+------+
2 row(s)

Por que EXISTS retorna Alma e Cara

  1. Duas tabelas são criadas: clientes armazena três pessoas, pedidos armazena três pedidos vinculados por cliente_id.
  2. WHERE EXISTS (SELECT 1 FROM pedidos o WHERE o.cliente_id = c.id) executa a subconsulta uma vez para cada linha de cliente. Se pelo menos um pedido correspondente existir, o cliente passa no filtro.
  3. SELECT 1 dentro da subconsulta é uma convenção: o banco ignora a lista de seleção e apenas verifica se alguma linha atende à condição.
  4. Ben não tem pedidos, então sua subconsulta retorna zero linhas e EXISTS é avaliado como falso. Alma e Cara têm pelo menos um pedido cada e aparecem no resultado.

Ao contrário de um INNER JOIN, EXISTS nunca duplica a linha externa. Alma tem dois pedidos, mas aparece uma vez porque EXISTS só pergunta “existe correspondência?” e não “quantas?”.

NOT EXISTS vs NOT IN: a armadilha do NULL

SQL NOT EXISTS (escrito como NOT EXISTS) é a escolha segura para anti-joins em colunas anuláveis. Quando uma subconsulta retorna valores NULL, NOT IN produz resultados inesperados porque qualquer comparação com NULL gera desconhecido:

-- Suponha que pedidos tenha uma linha com cliente_id NULL
-- NOT IN remove silenciosamente TODAS as linhas
SELECT nome FROM clientes
WHERE id NOT IN (SELECT cliente_id FROM pedidos);

-- NOT EXISTS continua funcionando corretamente
SELECT nome FROM clientes c
WHERE NOT EXISTS (
  SELECT 1 FROM pedidos o WHERE o.cliente_id = c.id
);

NOT IN trata id NOT IN (1, 3, NULL) como id <> 1 AND id <> 3 AND id <> NULL. A última comparação é sempre desconhecida, então todo o predicado nunca é verdadeiro e zero linhas retornam. NOT EXISTS evita isso porque verifica existência de linhas, não igualdade de valores.

Erros comuns com SQL EXISTS

Predicado de correlação ausente

Se a subconsulta não estiver correlacionada com a linha externa, EXISTS vira um teste constante: quando qualquer pedido existe, todo cliente corresponde.

-- Errado: sem correlação com a linha externa
SELECT c.nome FROM clientes c
WHERE EXISTS (SELECT 1 FROM pedidos);

-- Certo: correlacionado por cliente_id
SELECT c.nome FROM clientes c
WHERE EXISTS (SELECT 1 FROM pedidos o WHERE o.cliente_id = c.id);

Usar JOIN quando só a existência importa

Um INNER JOIN retorna uma linha por correspondência, então clientes com múltiplos pedidos aparecem várias vezes.

SELECT c.nome
FROM clientes c
JOIN pedidos o ON o.cliente_id = c.id;

Use EXISTS a menos que você precise de colunas de pedidos.

EXISTS vs IN vs JOIN

AbordagemUse quando…
EXISTS (subquery)Verificar existência de linhas relacionadas; a subconsulta é correlacionada
NOT EXISTS (subquery)Anti-join, especialmente com chaves estrangeiras anuláveis
IN (subquery)Comparar com um conjunto pequeno de valores não nulos
INNER JOINVocê precisa de colunas das duas tabelas no resultado
LEFT JOIN ... IS NULLAlternativa de anti-join; às vezes preferida pelo otimizador

Regra: use EXISTS para perguntas do tipo “existe uma linha relacionada?”. Troque para JOIN quando precisar de dados dos dois lados.

Notas de desempenho

EXISTS costuma ser planejado como semi-join. Um índice na coluna de correlação (pedidos.cliente_id aqui) permite que o mecanismo procure a primeira linha correspondente por cliente em vez de varrer pedidos. NOT EXISTS se beneficia do mesmo índice. Se EXISTS estiver lento, verifique o plano de execução e se a coluna de correlação está indexada.

FAQ

EXISTS para na primeira correspondência?

Logicamente, o resultado vira verdadeiro assim que existe uma linha válida. Muitos bancos implementam isso como semi-join com parada na primeira correspondência, mas a estratégia exata depende do otimizador. Consulte o plano de execução se precisar confirmar o que seu banco faz.

EXISTS pode ser usado em UPDATE e DELETE?

Sim. UPDATE ... WHERE EXISTS (subquery) e DELETE ... WHERE EXISTS (subquery) restringem quais linhas são modificadas ou removidas com base na existência de linhas relacionadas. Mantenha o mesmo predicado de correlação que você usaria em um SELECT, para que cada linha-alvo verifique apenas suas linhas relacionadas.

Qual é a diferença entre EXISTS e IN em SQL?

EXISTS verifica se uma subconsulta (geralmente correlacionada) retorna alguma linha. IN compara um valor com um conjunto de resultados. EXISTS funciona naturalmente com correlação da consulta externa, e muitos mecanismos podem parar após a primeira correspondência; IN é mais adequado para listas literais pequenas ou verificações de pertencimento não correlacionadas. Para anti-joins em colunas anuláveis, prefira NOT EXISTS em vez de NOT IN.