Tutorial de subconsultas SQL: tipos, subconsultas correlacionadas e EXISTS com exemplos

  1. Início
  2. Blog
  3. Bancos de dados e SQL

Bancos de dados e SQL

Tutorial de subconsultas SQL: Tipos, subconsultas correlacionadas e EXISTS com exemplos

Uma subconsulta é uma consulta dentro de outra consulta. Aprenda cada tipo e onde ele se encaixa, entenda as subconsultas correlacionadas corretamente e evite a armadilha NOT IN que silenciosamente não retorna nenhuma linha.

9 min de leitura
Tutorial de subconsultas SQL: tipos, subconsultas correlacionadas e EXISTS com exemplos

Principais pontos

  • Uma subconsulta é uma consulta aninhada dentro de outra, e a consulta externa usa seu resultado.
  • As subconsultas podem aparecer em WHERE, FROM, SELECT e HAVING.
  • Uma subconsulta correlacionada refere-se à linha externa, portanto é avaliada logicamente por linha.
  • Prefira NOT EXISTS a NOT IN quando NULLs forem possíveis.

Algumas perguntas levam duas etapas para serem respondidas. “Quais pedidos estão acima da média?” exige que você encontre a média primeiro e depois compare cada pedido com ela. Subconsultas SQL permitem escrever ambas as etapas como uma instrução.

Este tutorial cobre cada tipo usando duas pequenas tabelas. Executamos essas consultas no SQLite 3.50 e os resultados mostrados vêm dessa execução. A sintaxe de subconsulta padrão é amplamente suportada e apontamos os locais onde os bancos de dados diferem.

O que é uma subconsulta?

Uma subconsulta, também chamada de consulta interna ou aninhada, é uma instrução SELECT colocada dentro de outra instrução, entre colchetes. Conceitualmente, a consulta interna produz um resultado que a consulta externa utiliza.

SELECT id, total
FROM orders
WHERE total > (SELECT AVG(total) FROM orders);

As tabelas de amostra

CREATE TABLE customers (id INT, name TEXT, city TEXT);
CREATE TABLE orders (id INT, customer_id INT, total INT);

INSERT INTO customers VALUES
 (1,'Asha','Pune'), (2,'Ben','Delhi'), (3,'Chitra','Pune'), (4,'Dev','Goa');

INSERT INTO orders VALUES
 (101,1,1200), (102,1,800), (103,2,300), (104,3,2500), (105,3,100);

Observe que Dev não tem pedidos. Esse detalhe é importante para vários exemplos abaixo.

Visão geral dos tipos de subconsulta

Tipo Devoluções Localização típica Operadores comuns
Escalar Um valor WHERE, SELECT =, >, <
Várias linhas Uma coluna, muitas linhas ONDE IN, ANY, ALL
Tabela (derivada) Muitas colunas e linhas DE Usado como uma mesa
Correlacionado Depende da linha externa WHERE, SELECT Qualquer um e EXISTS

Subconsultas escalares

Uma subconsulta escalar retorna exatamente um valor, portanto você pode usá-la sempre que um único valor fizer sentido. Aqui está a consulta anterior, com seu resultado. A média de todos os pedidos é 980:

SELECT id, total
FROM orders
WHERE total > (SELECT AVG(total) FROM orders);

-- 101 | 1200
-- 104 | 2500

Você não poderia escrever WHERE total > AVG(total), porque agregações não são permitidas diretamente em WHERE. A subconsulta contorna isso calculando o valor separadamente.

E se uma subconsulta “escalar” retornar várias linhas? A maioria dos bancos de dados, incluindo PostgreSQL, MySQL e SQL Server, gera um erro. SQLite não. Em nosso teste, ele usou silenciosamente apenas a primeira linha, o que poderia esconder um bug real, portanto não dependa desse comportamento.

DENTRO e NÃO DENTRO

Quando a consulta interna retornar uma lista, use IN. Isso encontra clientes com pelo menos um pedido acima de 1.000:

SELECT name FROM customers
WHERE id IN (SELECT customer_id FROM orders WHERE total > 1000);

-- Asha
-- Chitra

NOT IN faz o oposto, encontrando clientes que nunca fizeram pedidos:

SELECT name FROM customers
WHERE id NOT IN (SELECT customer_id FROM orders);

-- Dev

Isso funciona aqui, mas NOT IN tem um caso extremo perigoso, que abordaremos abaixo.

Subconsultas em FROM

Uma subconsulta em FROM atua como uma tabela temporária, geralmente chamada de tabela derivada. É útil quando você precisa agregar duas vezes, como nesta consulta de gasto médio por cliente em cada cidade:

SELECT city, AVG(spent) AS avg_spent
FROM (
  SELECT c.city, SUM(o.total) AS spent
  FROM customers c
  JOIN orders o ON o.customer_id = c.id
  GROUP BY c.id, c.city
) AS per_customer
GROUP BY city;

-- Delhi |  300.0
-- Pune  | 2300.0

A consulta interna totaliza cada cliente e a externa calcula a média desses totais. Dar um alias à tabela derivada é um bom hábito, pois alguns bancos de dados exigem isso. Se GROUP BY for novo para você, consulte SQL GROUP BY e HAVING.

Subconsultas em SELECT

Uma subconsulta escalar também pode adicionar uma coluna calculada. Este conta pedidos por cliente:

SELECT name,
       (SELECT COUNT(*) FROM orders o WHERE o.customer_id = c.id) AS order_count
FROM customers c;

-- Asha   | 2
-- Ben    | 1
-- Chitra | 2
-- Dev    | 0

Dev aparece com zero. Uma junção interna teria descartado Dev completamente, porque Dev não possui linhas correspondentes. Você poderia obter o mesmo resultado com LEFT JOIN e GROUP BY, que explicamos em Explicação de junções SQL.

Subconsultas correlacionadas

O exemplo anterior também está correlacionado, pois a consulta interna refere-se a c.id da consulta externa. Uma subconsulta não correlacionada pode ser executada sozinha. Um correlacionado não pode, porque depende de um valor da linha externa atual.

Isso encontra pedidos acima da média do mesmo cliente:

SELECT o.id, o.customer_id, o.total
FROM orders o
WHERE o.total > (
  SELECT AVG(i.total)
  FROM orders i
  WHERE i.customer_id = o.customer_id
);

-- 101 | 1 | 1200
-- 104 | 3 | 2500

Aqui está como lê-lo. Para o pedido 101, a consulta interna calcula a média dos pedidos do cliente 1 (1.200 e 800), o que resulta em 1.000. Como 1.200 é maior, a linha é mantida. Para o pedido 102, aplica-se a mesma média, mas 800 não é maior, por isso é descartado. O pedido único do cliente 2 é igual à sua própria média, portanto também é descartado.

Recurso Não correlacionado Correlacionado
Usa valores de consulta externa? Não Sim
Pode funcionar sozinho? Sim Não
Execução lógica Uma vez Uma vez por linha externa
Risco de desempenho Geralmente baixo Pode ficar no alto de mesas grandes

“Uma vez por linha” descreve a lógica, e os otimizadores de consulta geralmente os reescrevem em algo muito mais eficiente. Ainda assim, vale a pena investigar uma subconsulta correlacionada lenta. Um índice na coluna interna, aqui orders.customer_id, pode fazer uma grande diferença. Consulte explicação dos índices SQL.

EXISTE e NÃO EXISTE

EXISTS faz uma pergunta sim ou não: a consulta interna retorna pelo menos uma linha? Pode parar na primeira partida, e o que você seleciona dentro não importa, e é por isso que as pessoas costumam escrever SELECT 1.

SELECT c.name FROM customers c
WHERE EXISTS (SELECT 1 FROM orders o WHERE o.customer_id = c.id);

-- Asha, Ben, Chitra

SELECT c.name FROM customers c
WHERE NOT EXISTS (SELECT 1 FROM orders o WHERE o.customer_id = c.id);

-- Dev

A armadilha NOT IN NULL

Este é um dos bugs de subconsulta mais comuns. Criamos uma tabela com um ID real e um NULL e, em seguida, executamos os dois formulários nela:

CREATE TABLE r (customer_id INT);
INSERT INTO r VALUES (1), (NULL);

SELECT name FROM customers
WHERE id NOT IN (SELECT customer_id FROM r);
-- (no rows at all)

SELECT c.name FROM customers c
WHERE NOT EXISTS (SELECT 1 FROM r WHERE r.customer_id = c.id);
-- Ben, Chitra, Dev

Por que o primeiro não retorna nada? id NOT IN (1, NULL) significa id <> 1 AND id <> NULL. Qualquer comparação com NULL é desconhecida e não verdadeira, portanto, toda a condição nunca pode ser verdadeira. Não há mensagem de erro nem aviso, apenas um resultado vazio que parece plausível.

Dica: Use NOT EXISTS para anti-junções por padrão. Se você usar NOT IN, adicione WHERE customer_id IS NOT NULL dentro da subconsulta.

Subconsulta vs JOIN vs CTE

Muitas consultas podem ser escritas de mais de uma maneira. Anteriormente, encontramos clientes com um pedido grande usando IN. Aqui está o mesmo resultado com uma junção:

SELECT DISTINCT c.name FROM customers c
JOIN orders o ON o.customer_id = c.id AND o.total > 1000;

-- Asha
-- Chitra

Observe o DISTINCT. Uma junção pode produzir duplicatas se um cliente tiver diversas correspondências, enquanto IN e EXISTS não podem.

Usar Quando
PARTICIPE Você precisa de colunas de ambas as tabelas
IN ou EXISTE Você só precisa filtrar se existe uma correspondência
Tabela derivada Você precisa agregar primeiro e permanece curto
CTE A lógica tem vários passos ou é usada mais de uma vez
Função de janela Você precisa de um valor por grupo ao lado de cada linha

O desempenho varia de acordo com o banco de dados e os dados, e os otimizadores modernos geralmente produzem planos semelhantes. Portanto, escolha primeiro a forma mais clara e, em seguida, marque EXPLAIN se ela for lenta.

Erros comuns

  • Usando = com um resultado de várias linhas. Use IN ou certifique-se de que a consulta interna retorne uma linha.
  • Usando NOT IN em uma coluna anulável. Você pode perder silenciosamente todas as linhas.
  • Usando aliases pouco claros. Em consultas correlacionadas, qualifique cada coluna ou ela poderá vincular-se à tabela errada.
  • Aninhamento profundo. Três níveis são difíceis de ler, então converta-os em CTEs.
  • Adicionar ORDER BY dentro. Geralmente não tem efeito no resultado externo e alguns bancos de dados o rejeitam.

Considerações finais

As subconsultas são uma forma natural de expressar perguntas em duas etapas. Comece com subconsultas escalares e IN, depois pratique as correlacionadas raciocinando através de uma única linha externa de cada vez. E lembre-se de NOT EXISTS sempre que NULLs forem possíveis. Para praticar, escreva uma pergunta de três maneiras, com uma subconsulta, uma junção e um CTE, e compare os resultados e a legibilidade.

Perguntas frequentes

O que é uma subconsulta em SQL?

Uma subconsulta é uma instrução SELECT aninhada dentro de outra instrução SQL, entre colchetes. A instrução externa usa seu resultado como um valor, uma lista ou uma tabela.

O que é uma subconsulta correlacionada?

É uma subconsulta que se refere a uma coluna da consulta externa. Depende da linha externa atual, portanto não pode ser executado sozinho.

O que é mais rápido, uma subconsulta ou um JOIN?

Depende do banco de dados e dos dados, e os otimizadores geralmente produzem planos semelhantes. Escreva primeiro a versão mais clara e depois compare os planos de execução se o desempenho for importante.

Qual é a diferença entre IN e EXISTS?

IN compara um valor com uma lista de resultados. EXISTS verifica se alguma linha foi retornada. Eles se comportam de maneira diferente com NULLs quando negados.

Uma subconsulta pode retornar várias colunas?

Sim, quando usado em FROM como uma tabela derivada, e alguns bancos de dados também permitem comparações de linhas. Uma subconsulta escalar deve retornar apenas uma coluna e uma linha.

Continue aprendendo

Trabalhe com Stack Quora

Precisa de ajuda com testes, WordPress ou SEO?

Ajudamos as equipes a fornecer software melhor e a expandir seus sites. Conte-nos no que você está trabalhando e sugeriremos um próximo passo claro.

Continue lendo

Mais guias sobre testes, IA e desenvolvimento moderno

Gostou deste artigo? Escolha um tópico e vá direto para nossos tutoriais mais recentes.

Inteligência artificial

Explicação das alucinações de IA: por que os modelos de linguagem inventam as coisas e como reduzi-las

Explicação das alucinações de IA: o que são, por que os modelos de linguagem as produzem, tipos comuns, como reduzi-las com aterramento e instruções…

Leia o artigo →
DevOps

Variáveis de ambiente explicadas: arquivos .env, segredos e configuração bem feitos

Variáveis de ambiente explicadas com exemplos de Python que executamos: leitura e configuração, por que cada valor é uma string, arquivos .env, hera…

Leia o artigo →
Desenvolvimento de software

Explicação da limitação de taxa de API: algoritmos, cabeçalhos e tratamento de erros 429

Limitação de taxa de API explicada com uma simulação que executamos: token bucket, janela fixa, janela deslizante, 429 respostas, cabeçalhos de limi…

Leia o artigo →
Automação de testes

Zombando em Python: unittest.mock, patch e os erros que fazem os testes mentirem

Zombaria em Python com unittest.mock, testado: Mock e return_value, side_effect, patch onde um nome é usado, especificação e autospec, zombaria de d…

Leia o artigo →
Teste de software

Cobertura de código explicada: cobertura de linha versus filial e o que 100% realmente significa

Cobertura de código explicada com uma execução cobertura.py real: cobertura de linha, ramal e condição, leitura de um relatório, por que 100% de cob…

Leia o artigo →
Bancos de dados e SQL

DELETE vs TRUNCATE vs DROP em SQL: diferenças, reversão e quando usar cada um

DELETE vs TRUNCATE vs DROP explicado com SQL testado: o que cada um remove, rollback, aciona, redefinição de identidade, velocidade e como PostgreSQ…

Leia o artigo →
Bancos de dados e SQL

Exibições SQL explicadas: crie, use e evite as armadilhas, com exemplos

Visualizações SQL explicadas com consultas que executamos: criação de uma visualização, resultados ao vivo, ocultação de colunas, desempenho, visual…

Leia o artigo →
Desenvolvimento de software

Interface TypeScript vs tipo: diferenças reais, testadas com o compilador

Interface TypeScript versus tipo, verificada com o compilador real: o que ambos podem fazer, o que apenas um pode fazer, mesclagem de declarações, e…

Leia o artigo →
Desenvolvimento de software

Python *args e **kwargs explicados: funções flexíveis com exemplos claros

Python *args e **kwargs explicados com o código que executamos: empacotamento e descompactação, ordem dos parâmetros, argumentos somente de palavras…

Leia o artigo →
Desenvolvimento de software

Funções Python Lambda: sintaxe, usos reais e quando não usá-los

Funções lambda do Python explicadas com o código que executamos: sintaxe, classificação com chave, mapa e filtro, fechamentos, armadilha de ligação …

Leia o artigo →
Inteligência artificial

O que são agentes de IA? Como eles funcionam, com um loop de agente simples que você pode executar

O que são agentes de IA? Um guia simples para o loop do agente, ferramentas, memória e proteções, com um pequeno loop Python que executamos, riscos …

Leia o artigo →
DevOps

Monitoramento versus observabilidade: a verdadeira diferença, com logs, métricas e rastreamentos

Explicação do monitoramento versus observabilidade: falhas conhecidas e desconhecidas, logs, métricas e rastreamentos, um passo a passo do incidente…

Leia o artigo →
DevOps

Docker Volumes vs Bind Mounts: persista os dados do contêiner da maneira certa

Volumes Docker versus montagens de ligação explicadas: por que os dados do contêiner desaparecem, volumes nomeados, montagens de ligação, tmpfs, exe…

Leia o artigo →
Automação de testes

TDD vs BDD: principais diferenças, um recurso criado de ambas as maneiras e quando usar cada um

TDD vs BDD explicado com um recurso de desconto construído em ambos os sentidos: refatoração vermelho-verde com o código que executamos, cenários da…

Leia o artigo →
Teste de software

Teste de aceitação do usuário (UAT): processo, lista de verificação e exemplos reais

Explicação do teste de aceitação do usuário: quem o executa, como ele difere do controle de qualidade, um processo de sete etapas, um modelo de caso…

Leia o artigo →
Bancos de dados e SQL

Valores SQL NULL explicados: IS NULL, COALESCE, NULLIF e as armadilhas a serem evitadas

Valores SQL NULL explicados com consultas que executamos: lógica de três valores, IS NULL, COALESCE, NULLIF, como agregações, junções e classificaçã…

Leia o artigo →
Bancos de dados e SQL

Instrução SQL CASE WHEN: sintaxe, exemplos reais e erros comuns

SQL CASE WHEN explicado com consultas que executamos: formulários simples e pesquisados, ordem de condição, pivôs com SUM, truques COUNT, classifica…

Leia o artigo →
Desenvolvimento de software

O que é JWT? JSON Web Tokens explicados com um exemplo prático

O que é JWT? JSON Web Tokens explicados: as três partes, um token construído e adulterado no código que executamos, JWT vs sessões, armazenamento, t…

Leia o artigo →
Desenvolvimento de software

Loop de eventos JavaScript explicado: pilha de chamadas, microtarefas e temporizadores com exemplos

O loop de eventos JavaScript explicado com o código que executamos: pilha de chamadas, filas de tarefas e microtarefas, por que setTimeout 0 não é i…

Leia o artigo →
Desenvolvimento de software

Geradores Python e rendimento explicado: economize memória com exemplos reais

Geradores Python e rendimento explicados com o código que executamos: como eles pausam e reiniciam, medem economia de memória, pipelines, sequências…

Leia o artigo →
Inteligência artificial

O que são incorporações? Similaridade de vetores e cossenos explicada de forma simples

O que são incorporações? Um guia em linguagem simples para vetores, similaridade de cossenos e pesquisa semântica, com um pequeno exemplo que execut…

Leia o artigo →
DevOps

Implantação azul-verde vs canário: diferenças, compensações e como escolher

Explicação da implantação azul-verde versus canário: como cada um funciona, custo, risco e reversão, agrupamento estável de usuários que testamos, m…

Leia o artigo →
Automação de testes

Testes instáveis: o que os causa e como corrigi-los para sempre

Testes instáveis explicados com demonstrações que executamos: as principais causas, como confirmar e diagnosticar uma, soluções adequadas para tempo…

Leia o artigo →
Teste de software

Teste de carga versus teste de estresse: diferenças, métricas e exemplos k6

Teste de carga versus teste de estresse explicado: o que cada um prova, testes de pico e absorção, as métricas que importam, por que as médias engan…

Leia o artigo →
Teste de software

Caso de teste versus cenário de teste: principais diferenças, exemplos e quando usar cada um

Caso de teste vs cenário de teste explicado: definições claras, uma tabela lado a lado, um recurso de redefinição de senha retirado de cenários para…

Leia o artigo →
Bancos de dados e SQL

Otimização de consultas SQL: leia planos de execução e corrija consultas lentas, com intervalos

Otimização de consulta SQL testada em um milhão de linhas: leia planos de execução, adicione os índices corretos, corrija filtros não sargáveis, use…

Leia o artigo →
Bancos de dados e SQL

SQL UNION vs UNION ALL: diferenças, exemplos e desempenho testado

SQL UNION vs UNION ALL explicado com consultas que executamos: duplicatas, as regras que ambos seguem, um bug de dados real, desempenho medido, INTE…

Leia o artigo →
Desenvolvimento de software

Tutorial da API JavaScript Fetch: GET, POST, tratamento de erros e tempos limite

Um tutorial da API JavaScript Fetch testado em um servidor local: GET e POST, por que 404 não lança, um auxiliar reutilizável, strings de consulta, …

Leia o artigo →
Desenvolvimento de software

Manipulação de arquivos Python: leitura, gravação, CSV, JSON e pathlib com exemplos

Manipulação de arquivos Python explicada com código que executamos: modos abertos, com, leitura de arquivos grandes, CSV, JSON, pathlib, arquivos bi…

Leia o artigo →
Desenvolvimento de software

Classes e objetos Python: tutorial OOP com exemplos reais

Classes e objetos Python explicados com código que executamos: __init__, métodos, atributos de classe vs instância, herança, propriedades, métodos d…

Leia o artigo →
Inteligência artificial

O que é um modelo de linguagem grande? Como funcionam os LLMs, explicado sem exageros

O que é um modelo de linguagem grande? LLMs explicados em linguagem simples: tokens, incorporações, atenção, treinamento, temperatura e janelas de c…

Leia o artigo →
DevOps

Comandos Linux para iniciantes: o guia essencial do terminal com exemplos

Comandos Linux para iniciantes, agrupados por tarefa: navegação, arquivos, visualização de logs, grep, pipes, permissões, processos e arquivos, com …

Leia o artigo →
Automação de testes

Selenium vs Playwright: principais diferenças, código comparado e qual escolher

Selenium vs Playwright comparado: arquitetura, espera, suporte a navegador e linguagem, depuração, o mesmo teste em ambas as ferramentas, conselhos …

Leia o artigo →
Teste de software

Particionamento de equivalência: técnica de design de teste explicada com exemplos resolvidos

Explicação do particionamento de equivalência: como encontrar partições válidas e inválidas, exemplos resolvidos para idades, senhas e cupons, teste…

Leia o artigo →
Teste de software

Gravidade vs Prioridade em Testes de Software: Diferenças, Matriz e Exemplos Reais

Explicação da gravidade versus prioridade em testes de software: definições claras, uma matriz de quatro vias com exemplos, escalas de nível, quem d…

Leia o artigo →
Bancos de dados e SQL

Tutorial de subconsultas SQL: tipos, subconsultas correlacionadas e EXISTS com exemplos

Um tutorial de subconsultas SQL com consultas que executamos: subconsultas escalares, IN, FROM e SELECT, subconsultas correlacionadas, EXISTS, a arm…

Leia o artigo →
Bancos de dados e SQL

Transações SQL e ACID explicados: COMMIT, ROLLBACK e níveis de isolamento

Transações SQL e ACID explicadas com uma transferência bancária que executamos: COMMIT, ROLLBACK, savepoints, locks, níveis de isolamento, leituras …

Leia o artigo →
Desenvolvimento de software

Git merge vs rebase: diferenças, exemplos e quando usar cada um

Git merge vs rebase explicado com saída real do log, uma tabela comparativa, a regra de ouro do rebase e um fluxo de trabalho seguro para equipes.

Leia o artigo →
Desenvolvimento de software

List comprehension em Python: sintaxe, exemplos e quando evitar

List comprehension em Python explicada: sintaxe, filtros, if-else, laços aninhados, tempos medidos e quando um laço for comum é a melhor escolha.

Leia o artigo →
Bancos de dados e SQL

SQL JOIN explicado com exemplos (Inner, Left, Right, Full)

Entenda os joins SQL passo a passo com tabelas e consultas de exemplo: INNER, LEFT, RIGHT, FULL, CROSS e SELF, além de erros comuns e dicas de desem…

Leia o artigo →