Subconsultas
Subconsultas
Imagine que você está em um supermercado e pergunta ao atendente: "Qual é o item mais caro da loja?" O atendente verifica e diz: "É um MacBook Pro." Então você complementa: "Quem são os clientes que compraram esse produto?" — O que você acabou de fazer é uma subconsulta: usar a resposta de uma pergunta para fazer a próxima. Subconsultas no SQL funcionam da mesma forma: usar o resultado de uma consulta como condição ou fonte de dados para outra consulta.
1. Conceitos Fundamentais
O que é uma Subconsulta?
Uma subconsulta (também chamada de consulta interna ou consulta aninhada) é uma consulta embutida dentro de outra instrução SQL. A consulta externa é chamada de consulta principal, e a consulta interna é chamada de subconsulta. A subconsulta é executada primeiro, e seu resultado é usado pela consulta principal.
-- Subconsulta: encontrar o funcionário com o maior salário
SELECT name, salary
FROM employees
WHERE salary = (SELECT MAX(salary) FROM employees);
Ordem de execução: Primeiro executa SELECT MAX(salary) FROM employees para obter 20000, depois executa a consulta externa para encontrar o(s) funcionário(s) com salário = 20000.
Classificação de Subconsultas
Com base onde a subconsulta aparece, existem três tipos principais:
| Tipo | Localização | Propósito | Retorna |
|---|---|---|---|
| Subconsulta WHERE | Na cláusula WHERE | Filtrar dados | Valor único ou lista |
| Subconsulta FROM (tabela derivada) | Na cláusula FROM | Atua como tabela temporária | Conjunto de resultados |
| Subconsulta SELECT (subconsulta escalar) | Na lista de colunas SELECT | Atua como coluna computada | Valor único |
Subconsulta WHERE
O uso mais comum. A subconsulta aparece na cláusula WHERE, determinando dinamicamente a condição de filtro.
-- Encontrar funcionários com salário acima da média
SELECT name, salary
FROM employees
WHERE salary > (SELECT AVG(salary) FROM employees);
Subconsultas podem retornar:
- Valor único (subconsulta escalar): usada com operadores de comparação como
=,>,< - Múltiplos valores (subconsulta de múltiplas linhas): usada com operadores como
IN,ANY,ALL
-- Retornar múltiplos valores: correspondência com IN
SELECT name, salary
FROM employees
WHERE department_id IN (
SELECT id FROM departments WHERE location = 'Beijing'
);
Subconsulta FROM (Tabela Derivada)
A subconsulta aparece na cláusula FROM, efetivamente criando uma tabela temporária (também chamada de tabela derivada). Deve receber um alias.
-- Primeiro calcular o salário médio de cada departamento, depois filtrar
SELECT dept_name, avg_salary
FROM (
SELECT d.name AS dept_name, AVG(e.salary) AS avg_salary
FROM employees e
JOIN departments d ON e.department_id = d.id
GROUP BY d.name
) AS dept_avg
WHERE avg_salary > 12000;
AS dept_avg acima), caso contrário o SQL lançará um erro.
Subconsulta SELECT (Subconsulta Escalar)
A subconsulta aparece na lista de colunas SELECT como uma coluna computada. Cada vez que a consulta principal processa uma linha, a subconsulta é executada uma vez.
-- Exibir o salário de cada funcionário e o salário médio da empresa
SELECT name, salary,
(SELECT AVG(salary) FROM employees) AS avg_salary
FROM employees;
Saída:
name salary avg_salary
-------- -------- ----------
Zhang San 15000.00 14000.00
Li Si 18000.00 14000.00
Wang Wu 12000.00 14000.00
Zhao Liu 13000.00 14000.00
Qian Qi 20000.00 14000.00
Sun Ba 11000.00 14000.00
Zhou Jiu 9000.00 14000.00
Wu Shi 14000.00 14000.00
EXISTS e NOT EXISTS
EXISTS verifica se uma subconsulta retorna pelo menos uma linha. Não se importa com quais valores são retornados — apenas se "existem resultados".
-- Encontrar departamentos que possuem funcionários
SELECT d.name
FROM departments d
WHERE EXISTS (
SELECT 1 FROM employees e WHERE e.department_id = d.id
);
-- Encontrar departamentos sem funcionários
SELECT d.name
FROM departments d
WHERE NOT EXISTS (
SELECT 1 FROM employees e WHERE e.department_id = d.id
);
SELECT 1 em EXISTS é uma prática convencional porque EXISTS só se importa com "se as linhas existem", não com os valores das colunas. Usar SELECT * ou SELECT NULL tem o mesmo efeito.
Comparação de Desempenho: Subconsulta vs JOIN
| Item de Comparação | Subconsulta | JOIN |
|---|---|---|
| Legibilidade | Mais próxima do pensamento em linguagem natural | Requer compreensão da lógica de junção |
| Desempenho | Pouca diferença em casos simples; subconsultas correlacionadas podem ser mais lentas | Geralmente melhor, com otimizador especializado |
| Flexibilidade | Pode usar resultados de funções de agregação como condições | Requer GROUP BY |
| Recomendado para | "Encontrar o maior", "encontrar inexistente" | "Exibir com associação", "mesclagem de múltiplas tabelas" |
EXPLAIN para analisar e otimizar quando surgirem problemas de desempenho.
2. Sintaxe/Uso Básico
Sintaxe de Subconsulta WHERE
-- Subconsulta escalar (retorna valor único)
SELECT nome_coluna FROM tabela
WHERE coluna operador_comparação (SELECT função_agregação FROM tabela);
-- Subconsulta de múltiplas linhas (retorna lista)
SELECT nome_coluna FROM tabela
WHERE coluna IN (SELECT coluna FROM tabela WHERE condição);
Sintaxe de Subconsulta FROM
SELECT nome_coluna
FROM (SELECT ... FROM ... WHERE ...) AS alias
WHERE condição;
Sintaxe de Subconsulta SELECT Escalar
SELECT coluna1, coluna2,
(SELECT função_agregação FROM tabela WHERE condição) AS alias
FROM tabela;
Sintaxe EXISTS
SELECT nome_coluna FROM tabela_A a
WHERE EXISTS (SELECT 1 FROM tabela_B b WHERE b.chave_estrangeira = a.chave_primaria);
a.id), então a subconsulta é executada uma vez para cada linha da consulta externa.
Exemplo: Encontrar Funcionários com Salário Acima da Média (Dificuldade ⭐)
SELECT name, salary
FROM employees
WHERE salary > (SELECT AVG(salary) FROM employees)
ORDER BY salary DESC;
Saída:
name salary
-------- --------
Qian Qi 20000.00
Li Si 18000.00
Zhang San 15000.00
Wu Shi 14000.00
Primeiro calcula o salário médio (aproximadamente 14000), depois filtra funcionários acima da média.
Exemplo: Encontrar o Funcionário Mais Bem Pago de Cada Departamento (Dificuldade ⭐⭐)
SELECT e.name, d.name AS department, e.salary
FROM employees e
JOIN departments d ON e.department_id = d.id
WHERE e.salary = (
SELECT MAX(e2.salary)
FROM employees e2
WHERE e2.department_id = e.department_id
);
Saída:
name department salary
-------- ---------- --------
Qian Qi Technology 20000.00
Wang Wu Marketing 12000.00
Wu Shi Finance 14000.00
e.department_id na subconsulta referencia a linha atual da consulta externa. A subconsulta é executada uma vez para cada linha externa, encontrando o maior salário naquele departamento do funcionário, e então verifica se o funcionário atual é o mais bem pago.
Exemplo: Usar Tabela Derivada para Analisar Níveis Salariais por Departamento (Dificuldade ⭐⭐⭐)
SELECT dept_name, emp_count, avg_salary,
CASE
WHEN avg_salary >= 15000 THEN 'Alto'
WHEN avg_salary >= 12000 THEN 'Médio'
ELSE 'Precisa melhorar'
END AS nivel
FROM (
SELECT d.name AS dept_name,
COUNT(e.id) AS emp_count,
AVG(e.salary) AS avg_salary
FROM departments d
LEFT JOIN employees e ON d.id = e.department_id
GROUP BY d.id, d.name
) AS dept_stats
WHERE emp_count > 0
ORDER BY avg_salary DESC;
Saída:
dept_name emp_count avg_salary nivel
--------- --------- ---------- ----------------
Technology 3 17666.67 Alto
Finance 2 13500.00 Médio
Marketing 2 11500.00 Precisa melhorar
3. Casos de Uso Comuns
Caso 1: Encontrar Produtos Nunca Pedidos
SELECT p.name, p.category, p.price
FROM products p
WHERE NOT EXISTS (
SELECT 1 FROM orders o WHERE o.product_id = p.id
);
Saída (depende dos dados):
name category price
-------- -------- -------
Mac Mini Computer 4499.00
SELECT p.name, p.category, p.price
FROM products p
LEFT JOIN orders o ON p.id = o.product_id
WHERE o.id IS NULL;
Ambas as abordagens têm desempenho semelhante — escolha a com melhor legibilidade.
Caso 2: Encontrar Clientes cujo Valor de Pedido Excede a Média
SELECT customer_name, total_spent
FROM (
SELECT o.customer_name,
SUM(o.quantity * p.price) AS total_spent
FROM orders o
JOIN products p ON o.product_id = p.id
GROUP BY o.customer_name
) AS customer_totals
WHERE total_spent > (
SELECT AVG(o.quantity * p.price)
FROM orders o
JOIN products p ON o.product_id = p.id
);
Saída:
customer_name total_spent
------------- -----------
Xiao Li 14397.00
Xiao Gang 17998.00
Xiao Wang 11998.00
❓ Perguntas Frequentes
P: Quantos níveis de subconsultas podem ser aninhados? R: Não há limite teórico, mas na prática, recomenda-se não exceder 3 níveis. Muitos níveis indicam lógica de consulta excessivamente complexa — considere dividir em múltiplas etapas ou reescrever com JOINs.
P: Devo usar IN ou EXISTS? R: Quando o conjunto de resultados da subconsulta é pequeno e a tabela externa é grande, IN é mais eficiente; quando a tabela externa é pequena e a tabela da subconsulta é grande, EXISTS é mais eficiente (porque EXISTS para na primeira correspondência). A diferença é mínima para cenários simples — use EXPLAIN para comparar em casos complexos.
P: O que acontece se uma subconsulta escalar retornar múltiplas linhas? R: O banco de dados lançará um erro. Uma subconsulta escalar deve retornar exatamente uma linha e uma coluna. Se você não tiver certeza sobre a contagem de linhas, use
LIMIT 1ou uma função de agregação (comoMAX,MIN) para garantir um valor único.
P: Qual é a diferença entre subconsultas correlacionadas e não correlacionadas? R: Uma subconsulta não correlacionada é executada independentemente uma vez (por exemplo, "encontrar salário médio"), e seu resultado é usado pela consulta principal. Uma subconsulta correlacionada referencia colunas da consulta externa e é executada uma vez para cada linha externa (por exemplo, "encontrar o maior salário em cada departamento"). Subconsultas correlacionadas podem ser lentas com grandes conjuntos de dados — observe o desempenho.
📖 Resumo
- Uma subconsulta é uma consulta aninhada dentro de outra instrução SQL; a consulta interna é executada primeiro, e seu resultado é usado pela consulta externa
- Subconsulta WHERE: Usada para filtragem dinâmica com operadores como
IN,=,> - Subconsulta FROM (tabela derivada): Atua como fonte de dados temporária; deve ter um alias; adequada para "agregar primeiro, analisar depois"
- Subconsulta SELECT escalar: Atua como coluna computada, executada uma vez para cada linha externa
- EXISTS/NOT EXISTS: Só se importa se a subconsulta tem resultados; adequada para "encontrar associações existentes/faltantes"
- Subconsulta vs JOIN: Priorize legibilidade; use EXPLAIN para analisar problemas de desempenho
📝 Exercícios
Exercício 1 (⭐): Use uma subconsulta para encontrar todos os funcionários do departamento "Technology" (dica: primeiro use uma subconsulta para encontrar o ID do departamento Technology).
Exercício 2 (⭐⭐): Use uma subconsulta FROM (tabela derivada) para calcular a contagem de pedidos e o gasto total de cada cliente, e então filtre clientes cujo gasto total excede 5000.
Exercício 3 (⭐⭐⭐): Use EXISTS para encontrar "clientes que pediram todos os produtos" — ou seja, clientes para os quais não existe nenhum "produto não pedido" (dica: lógica de duplo NOT EXISTS, ou implemente de outra forma).
Próxima Lição
👉 10-set-operations - Operações com Conjuntos: Aprenda UNION, UNION ALL, INTERSECT e EXCEPT, e domine operações com conjuntos em múltiplos resultados de consultas!



