SQL: Sub Queries

작성자

카테고리:

← 피드로
DEV Community · Yuri Peixinho · 2026-07-22 개발(SW)

Introdução

Uma subquery é uma consulta SQL aninhada dentro de outra. Assim como uma função pode chamar outra função, um SELECT pode conter outro SELECT — e o resultado do interno é usado pelo externo para filtrar, calcular ou compor o resultado final.

Subqueries aparecem em três lugares possíveis: no SELECT, no FROM, ou no WHERE/HAVING. O comportamento muda dependendo do tipo de resultado que a subquery retorna — e é aí que a classificação em Scalar, Column, Row e Table faz sentido.

Para os exemplos, usaremos:

clientes: id, nome, cidade
pedidos:  id, cliente_id, valor, criado_em
produtos: id, nome, preco, categoria_id
categorias: id, nome

Enter fullscreen mode Exit fullscreen mode

Tipos por Resultado

Retorna exatamente um valor: uma linha, uma coluna. Pode ser usada em qualquer lugar onde um valor único seria esperado — no SELECT, no WHERE, ou no HAVING.

Scalar Subquery

-- Qual cliente fez o pedido de maior valor?
SELECT nome
FROM clientes
WHERE id = (
  SELECT cliente_id
  FROM pedidos
  ORDER BY valor DESC
  LIMIT 1
);

Enter fullscreen mode Exit fullscreen mode

A subquery interna retorna um único cliente_id — o do pedido mais caro. A externa usa esse valor para buscar o nome.

Scalar subqueries também funcionam diretamente no SELECT, como uma coluna calculada:

SELECT
  nome,
  (SELECT COUNT(*)FROM pedidosWHERE cliente_id= c.id)AS total_pedidos
FROM clientes c;

Enter fullscreen mode Exit fullscreen mode

Se uma scalar subquery retornar mais de uma linha, o banco lança um erro. Por isso, é comum garantir isso com LIMIT 1 ou usando funções de agregação.

Column Subquery

Retorna múltiplas linhas de uma única coluna — essencialmente uma lista de valores. É usada principalmente com os operadores INNOT INANY e ALL.

-- Clientes que fizeram pelo menos um pedido acima de 1000
SELECT nome
FROM clientes
WHERE id IN (
  SELECT DISTINCT cliente_id
  FROM pedidos
  WHERE valor > 1000
);

Enter fullscreen mode Exit fullscreen mode

A subquery retorna uma coluna com os IDs dos clientes elegíveis. O IN da query externa verifica se cada id está nessa lista.

O inverso com NOT IN é igualmente útil — mas exige cuidado: se a subquery retornar algum NULL, o NOT IN se comporta de forma contra-intuitiva e pode não retornar nenhuma linha. Nesse caso, prefira NOT EXISTS.

ANY e ALL são alternativas mais expressivas em certos cenários:

-- Produtos mais caros que QUALQUER produto da categoria 2
SELECT nome, preco
FROM produtos
WHERE preco > ANY (
  SELECT preco FROM produtos WHERE categoria_id= 2
);

-- Produtos mais caros que TODOS os produtos da categoria 2
SELECT nome, preco
FROM produtos
WHERE preco > ALL (
  SELECT preco FROM produtos WHERE categoria_id= 2
);

Enter fullscreen mode Exit fullscreen mode

Row Subquery

Retorna uma linha com múltiplas colunas. Menos comum, mas útil para comparar um conjunto de valores de uma vez só.

-- Encontra o produto que tem exatamente esse nome e preço
SELECT id
FROM produtos
WHERE (nome, preco)= (
  SELECT nome, preco FROM produtos WHERE id = 10
);

Enter fullscreen mode Exit fullscreen mode

A comparação de linha inteira evita escrever múltiplas condições AND. O suporte varia entre bancos — PostgreSQL e MySQL suportam bem; SQL Server tem suporte mais limitado.

Table Subquery

Retorna múltiplas linhas e colunas — um conjunto de dados completo que é tratado como se fosse uma tabela temporária. Também chamada de derived table ou inline view. Aparece no FROM e precisa de um alias.

-- Receita por cliente, mas filtrando apenas quem passou de 500
SELECT cliente_nome, receita_total
FROM (
  SELECT
    c.nomeAS cliente_nome,
    SUM(p.valor) AS receita_total
  FROM clientes c
  JOIN pedidos p ON p.cliente_id= c.id
  GROUP BY c.nome
) AS resumo
WHERE receita_total> 500;

Enter fullscreen mode Exit fullscreen mode

A subquery interna calcula a receita por cliente. A query externa trata esse resultado como uma tabela e aplica o filtro. Isso seria impossível diretamente porque WHERE é avaliado antes do GROUP BY — a table subquery contorna isso.

Tipos por Comportamento

Nested Subquery (Independente)

Uma subquery é nested quando pode ser executada de forma completamente independente da query externa. Ela não referencia nenhuma coluna da tabela externa — é avaliada uma única vez, e o resultado é passado para a query externa usar.

SELECT nome
FROM clientes
WHERE cidade= (
  SELECT cidade FROM clientes WHERE id= 1
);

Enter fullscreen mode Exit fullscreen mode

A subquery interna busca a cidade do cliente 1. Isso não depende de nenhum dado da query externa — roda uma vez, retorna 'São Paulo', e a query externa usa esse valor para filtrar todos os clientes.

Por serem independentes, nested subqueries são geralmente eficientes — o banco as executa uma única vez e reutiliza o resultado.

Correlated Subquery (Dependente)

Uma subquery é correlacionada quando referencia colunas da query externa. Isso significa que ela é reavaliada para cada linha que a query externa processa — o que pode ser custoso em tabelas grandes, mas permite expressar consultas que seriam impossíveis de outra forma.

-- Clientes cujo total de pedidos está acima da média geral
SELECT nome
FROM clientes c
WHERE (
  SELECT SUM(valor)
  FROM pedidos p
  WHERE p.cliente_id=  c.id-- referência à query externa
)> (
  SELECT AVG(valor) FROM pedidos
);

Enter fullscreen mode Exit fullscreen mode

A subquery WHERE p.cliente_id = c.id referencia c.id, que vem da query externa. Para cada cliente que a query externa examina, a subquery roda novamente com o id daquele cliente específico.

O uso mais clássico de correlated subquery é com EXISTS e NOT EXISTS:

-- Clientes que fizeram pelo menos um pedido
SELECT nome
FROM clientes c
WHERE EXISTS (
  SELECT 1
  FROM pedidos p
  WHERE p.cliente_id= c.id
);

-- Clientes que nunca fizeram nenhum pedido
SELECT nome
FROM clientes c
WHERE NOT EXISTS (
  SELECT 1
  FROM pedidos p
  WHERE p.cliente_id = c.id
);

Enter fullscreen mode Exit fullscreen mode

EXISTS retorna verdadeiro assim que encontra a primeira correspondência — não precisa contar nem trazer dados, apenas confirmar que existe ao menos uma linha. Por isso é mais eficiente que IN em muitos casos, especialmente quando a subquery pode retornar NULL.

Subquery vs JOIN

Muitas subqueries podem ser reescritas como JOINs, e vice-versa. A escolha entre os dois depende de legibilidade e, às vezes, de performance — embora os otimizadores modernos frequentemente gerem planos de execução idênticos para ambos.

-- Com subquery
SELECT nome FROM clientes
WHERE id IN (SELECT cliente_id FROM pedidos WHERE valor > 1000);

-- Com JOIN
SELECT DISTINCT c.nome
FROM clientes c
INNER JOIN pedidos p ON p.cliente_id = c.id
WHERE p.valor > 1000;

Enter fullscreen mode Exit fullscreen mode

O resultado é o mesmo. A subquery é mais próxima da forma como pensamos a pergunta em linguagem natural; o JOIN explicita a relação entre as tabelas. Use o que tornar a intenção mais clara — e prefira EXISTS a IN quando a subquery puder conter NULL ou retornar muitas linhas.

원문에서 계속 ↗

추출 본문 · 출처: dev.to · https://dev.to/yuripeixinho/sql-sub-queries-18c

코멘트

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다