
Quando alguém vê uma subquery pela primeira vez, normalmente acontece algo parecido.
Uma consulta aparece dentro de outra consulta.
Parênteses.
Outro SELECT.
Outro FROM.
E surge a sensação:
“Agora o SQL começou a ficar complicado.”
Mas uma subquery não precisa ser complicada.
Na verdade, muitas vezes ela aparece porque estamos tentando responder uma pergunta que possui outra pergunta dentro dela.
Parece estranho?
Então vamos começar pelo problema.
Imagine a seguinte pergunta
Você trabalha em um e-commerce.
O gestor pergunta:
Quais produtos possuem preço acima da média de preços do catálogo?
Não pense no SQL ainda.
Vamos entender a pergunta.
Para descobrir se um produto está acima da média, precisamos saber duas coisas:
1. Qual é o preço do produto?
2. Qual é a média de preço dos produtos?
Percebeu?
Existe uma pergunta principal:
Quais produtos estão acima da média?
Mas, para respondê-la, precisamos responder primeiro:
Qual é a média?
É exatamente aí que uma subquery começa a fazer sentido.
Primeiro, vamos descobrir a média
Imagine uma tabela produto com esta estrutura:
produto
----------------
ID_PRODUTO
NOME
PRECOPara descobrir o preço médio dos produtos, podemos escrever:
SELECT
AVG(PRECO)
FROM produto;Imagine que o resultado seja:
125.50Agora sabemos que o preço médio do catálogo é R$ 125,50.
Falta descobrir quais produtos possuem preço maior do que esse valor.
Poderíamos simplesmente escrever 125,50 na consulta?
Tecnicamente, poderíamos fazer algo assim:
SELECT
ID_PRODUTO,
NOME,
PRECO
FROM produto
WHERE PRECO > 125.50;A consulta funciona.
Mas existe um problema.
A média pode mudar.
Um produto novo pode ser cadastrado.
Um preço pode ser alterado.
Produtos podem ser removidos.
Se a média passar para R$ 138,20, nossa consulta continuará comparando com R$ 125,50.
Ou seja:
transformamos um valor dinâmico em um valor fixo.
Não é isso que queremos.
A consulta precisa descobrir a média sozinha
Então podemos substituir o número:
125.50pela própria consulta que calcula a média:
SELECT
AVG(PRECO)
FROM produtoE chegamos a:
SELECT
ID_PRODUTO,
NOME,
PRECO
FROM produto
WHERE PRECO > (
SELECT
AVG(PRECO)
FROM produto
);Pronto.
Temos uma subquery.
E talvez você tenha percebido que não aconteceu nada de extraordinário.
Nós apenas colocamos uma consulta dentro de outra.
Leia de dentro para fora
Uma forma simples de entender esse tipo de subquery é começar pela consulta interna.
Primeiro:
SELECT
AVG(PRECO)
FROM produto;Ela responde:
Qual é o preço médio?
Depois a consulta externa utiliza essa resposta:
SELECT
ID_PRODUTO,
NOME,
PRECO
FROM produto
WHERE PRECO > (...);Ela responde:
Quais produtos possuem preço maior do que esse valor?
Então podemos pensar assim:
Pergunta 1
Qual é a média?
↓
Resposta
R$ 125,50
↓
Pergunta 2
Quais produtos possuem preço acima de R$ 125,50?
A subquery apenas permite que o banco faça as duas etapas dentro da mesma consulta.
O problema não nasceu do comando
Esse ponto é importante.
Não começamos dizendo:
“Hoje vou utilizar uma subquery.”
Começamos com:
“Quais produtos possuem preço acima da média?”
Foi a pergunta que criou a necessidade da subquery.
Essa mudança de perspectiva ajuda muito quando você começa a estudar recursos intermediários de SQL.
Em vez de perguntar:
“Onde posso encaixar uma subquery?”
Pergunte:
Existe alguma informação que preciso descobrir antes de conseguir responder à pergunta principal?
Se a resposta for sim, talvez exista uma oportunidade para utilizar uma subquery.
Outro exemplo: pedidos acima do ticket médio
Imagine agora uma tabela pedido:
pedido
----------------
ID_PEDIDO
ID_CLIENTE
DATA_PEDIDO
VALOR_TOTALO gestor pergunta:
Quais pedidos possuem valor acima do ticket médio?
Antes de escrever SQL, pense.
Precisamos descobrir:
Qual é o ticket médio?
Depois:
Quais pedidos possuem
VALOR_TOTALacima desse valor?
Primeiro:
SELECT
AVG(VALOR_TOTAL)
FROM pedido;Depois:
SELECT
ID_PEDIDO,
ID_CLIENTE,
VALOR_TOTAL
FROM pedido
WHERE VALOR_TOTAL > (
SELECT
AVG(VALOR_TOTAL)
FROM pedido
);O raciocínio é exatamente o mesmo.
Mudou o contexto.
Não a lógica.
Subquery não significa necessariamente consulta avançada
Esse é um bloqueio comum.
A pessoa aprende:
SELECT.
Depois WHERE… depois JOIN… depois GROUP BY.
Quando chega em subquery, pensa:
“Agora entrei no SQL avançado.”
Só que a dificuldade de uma consulta não depende simplesmente da quantidade de recursos utilizados.
Podemos ter uma subquery muito simples.
E podemos ter um JOIN extremamente difícil de entender.
O que determina a dificuldade é o problema que estamos tentando resolver.
O resultado da subquery importa
Agora precisamos avançar um pouco no raciocínio.
Observe nossa condição:
WHERE PRECO > (
SELECT AVG(PRECO)
FROM produto
)O operador > compara um valor com outro valor.
Por isso nossa subquery precisa retornar um único valor.
Neste caso:
125.50Funciona perfeitamente.
Mas imagine isto:
WHERE PRECO > (
SELECT PRECO
FROM produto
)Agora a consulta interna provavelmente retorna vários preços.
Por exemplo:
59.90
89.90
129.90
199.90
...Então surge um problema.
O SQL precisaria interpretar algo como:
PRECO > qual desses valores?
Essa comparação não faz sentido dessa forma.
Antes de usar uma subquery, faça esta pergunta
O que a consulta interna vai retornar?
Pode ser:
- um único valor;
- uma lista de valores;
- várias linhas;
- várias colunas.
Essa resposta influencia diretamente a forma como a consulta externa precisa trabalhar.
No nosso exemplo, AVG() retorna apenas um valor.
Por isso a comparação com > funciona naturalmente.
Um erro muito comum: escrever tudo de uma vez
Imagine que você recebeu esta pergunta:
Quais produtos possuem preço acima da média?
Se tentar escrever a consulta inteira imediatamente, pode travar.
Existe um caminho mais simples.
Resolva primeiro a pergunta menor.
SELECT
AVG(PRECO)
FROM produto;Execute.
Veja o resultado.
Confirme se faz sentido.
Depois construa:
SELECT
ID_PRODUTO,
NOME,
PRECO
FROM produto
WHERE PRECO > 125.50;Teste novamente.
Só depois substitua o valor pela consulta:
SELECT
ID_PRODUTO,
NOME,
PRECO
FROM produto
WHERE PRECO > (
SELECT
AVG(PRECO)
FROM produto
);Essa forma de construir consultas é extremamente útil.
Você não tenta resolver tudo ao mesmo tempo.
Resolve pequenos problemas.
Depois conecta as soluções.
E se precisarmos comparar cada produto com sua categoria?
Agora vamos deixar o problema um pouco mais interessante.
Imagine a pergunta:
Quais produtos possuem preço acima da média da própria categoria?
Observe que a pergunta mudou.
Antes tínhamos:
média de todos os produtos.
Agora temos:
média da categoria daquele produto.
Isso significa que a referência não é mais uma única média geral.
Cada produto precisa ser comparado com uma média diferente.
Um notebook deve ser comparado com outros produtos da sua categoria.
Uma cadeira com produtos da categoria correspondente.
Um monitor com sua própria categoria.
Aqui entramos em um cenário em que podemos utilizar uma subquery correlacionada.
Por exemplo:
SELECT
P.ID_PRODUTO,
P.NOME,
P.PRECO
FROM produto P
WHERE P.PRECO > (
SELECT
AVG(P2.PRECO)
FROM produto P2
WHERE P2.ID_CATEGORIA = P.ID_CATEGORIA
);Agora a consulta interna depende do produto que está sendo analisado pela consulta externa.
Perceba que o conceito ficou mais avançado.
Mas o raciocínio continua vindo da pergunta.
Não tente decorar todos os tipos de subquery de uma vez
Quando alguém começa a estudar o assunto, pode encontrar rapidamente termos como:
- scalar subquery;
- correlated subquery;
- nested subquery;
- subquery no
FROM; - subquery no
SELECT; - subquery no
WHERE.
Tudo isso existe.
Mas tentar memorizar todas as classificações logo no começo pode atrapalhar mais do que ajudar.
Primeiro entenda a ideia principal:
Uma consulta pode produzir uma informação necessária para outra consulta.
Depois você começa a perceber onde cada estrutura faz sentido.
Subquery ou JOIN?
Outra pergunta comum é:
“Tudo que faço com subquery posso fazer com JOIN?”
Em muitos problemas existem diferentes formas de chegar ao mesmo resultado.
Mas isso não significa que você precise transformar imediatamente toda subquery em JOIN.
A pergunta mais útil no começo é:
Qual forma deixa o raciocínio mais claro?
Se a pergunta naturalmente parece:
“Descubra X e depois compare com Y”
uma subquery pode representar muito bem essa lógica.
Em outros cenários, principalmente quando precisamos trazer informações de várias tabelas para o resultado, um JOIN pode fazer mais sentido.
Não escolha uma técnica apenas porque parece mais avançada.
Escolha porque ela representa bem o problema.
E CTE?
Talvez você já tenha ouvido falar de CTE.
Ela começa normalmente com:
WITHE pode ser utilizada para organizar consultas em etapas.
Então surge outra pergunta:
“Quando uso CTE e quando uso subquery?”
Essa comparação merece um artigo próprio.
E é exatamente o que vamos fazer na próxima semana.
Por enquanto, quero que você guarde uma ideia:
subquery e CTE não são inimigas.
São formas diferentes de estruturar o raciocínio de uma consulta.
Um desafio para você
Imagine uma tabela pedido com:
ID_PEDIDO
ID_CLIENTE
DATA_PEDIDO
VALOR_TOTALSua pergunta é:
Quais pedidos possuem valor abaixo da média de todos os pedidos?
Não copie a consulta anterior.
Tente pensar.
Primeiro:
Qual informação preciso descobrir antes?
Depois:
Como comparo cada pedido com essa informação?
Quando conseguir responder essas duas perguntas, escreva a consulta.
Agora um desafio um pouco diferente
Imagine que você possui uma tabela produto:
ID_PRODUTO
NOME
PRECO
ID_CATEGORIAA pergunta é:
Qual é o produto mais caro do catálogo?
Você conseguiria resolver utilizando uma subquery?
Pense primeiro:
Qual informação preciso descobrir?
Provavelmente:
MAX(PRECO)Depois:
Quais produtos possuem exatamente esse preço?
Perceba novamente a estrutura:
uma pergunta dentro de outra pergunta.
Como saber se uma subquery pode ajudar?
Quando receber um problema, procure frases como:
acima da média…
abaixo da média…
maior que…
menor que…
igual ao maior…
clientes que possuem…
produtos que pertencem…
Não significa que toda pergunta desse tipo exige subquery.
Mas pode indicar que existe uma informação intermediária que precisa ser descoberta.
E essa é uma pista importante.
O verdadeiro aprendizado aqui não é o parêntese
Você poderia decorar:
SELECT ...
FROM ...
WHERE coluna > (
SELECT ...
);E ainda assim travar diante de um problema novo.
Por isso prefiro que você memorize outra coisa:
O que eu preciso descobrir primeiro para conseguir responder à pergunta principal?
Essa pergunta ajuda muito mais.
Porque, quando você consegue separar um problema em etapas, estruturas como subqueries começam a aparecer naturalmente.
Conclusão
Subquery parece complicada quando você começa olhando para o código.
Mas fica muito mais simples quando começa olhando para o problema.
No nosso exemplo, queríamos descobrir:
Quais produtos possuem preço acima da média?
Primeiro descobrimos a média.
Depois utilizamos essa informação para encontrar os produtos.
Só isso.
A subquery foi apenas a forma de juntar essas duas etapas dentro de uma única consulta.
Por isso, da próxima vez que encontrar um SELECT dentro de outro SELECT, não pense imediatamente:
“SQL avançado.”
Pergunte:
Qual pergunta a consulta interna está respondendo para a consulta externa?
Quando você consegue responder isso, boa parte da complexidade desaparece.
Próximo passo
Na Coleção A Arte da Query, você pratica SQL justamente dessa forma: começando por uma situação e uma pergunta que precisa ser respondida.
Você precisa interpretar o cenário, identificar quais informações são necessárias e decidir como construir a consulta.
Em alguns problemas, a solução passa por JOIN.
Outros, por agregações.
Em outros, uma subquery começa a fazer sentido.
Porque, no trabalho real, ninguém vai chegar até você dizendo:
“Hoje preciso que você pratique subquery.”
A pessoa vai trazer um problema.
E você precisará descobrir qual ferramenta SQL faz sentido para resolvê-lo.
É essa habilidade que transforma sintaxe em prática.
0 Comentários