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
- Duas tabelas são criadas:
clientesarmazena três pessoas,pedidosarmazena três pedidos vinculados porcliente_id. 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.SELECT 1dentro da subconsulta é uma convenção: o banco ignora a lista de seleção e apenas verifica se alguma linha atende à condição.- 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
| Abordagem | Use 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 JOIN | Você precisa de colunas das duas tabelas no resultado |
LEFT JOIN ... IS NULL | Alternativa 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.