No artigo anterior, vimos que uma subquery pode ser entendida de uma forma bastante simples:

uma consulta que produz uma informação necessária para outra consulta.

Mas, quando você começa a avançar em SQL, aparece outra estrutura:

WITH

A famosa CTE.

E junto dela surge uma dúvida quase inevitável:

“Quando devo usar CTE e quando devo usar subquery?”

Se você pesquisar essa pergunta, provavelmente encontrará respostas como:

“CTE é mais legível.”

Ou:

“Subquery é mais rápida.”

Ou ainda:

“Sempre prefira CTE.”

Eu não gosto muito dessas regras.

Porque elas tentam decidir a ferramenta antes de entender o problema.

Então vamos fazer diferente.

Vamos começar com uma pergunta de negócio.


Imagine este problema

Você trabalha em um e-commerce.

O gerente pergunta:

Quais clientes gastaram acima da média de gastos dos clientes?

Leia novamente porque existe um detalhe importante.

Não queremos descobrir:

Quais pedidos possuem valor acima da média dos pedidos?

Queremos descobrir:

Quais clientes possuem gasto total acima da média dos clientes?

Isso significa que precisamos realizar algumas etapas.

Primeiro:

descobrir quanto cada cliente gastou.

Depois:

calcular a média desses totais.

E finalmente:

encontrar os clientes acima dessa média.

Temos uma análise em etapas.


Primeiro problema: quanto cada cliente gastou?

Imagine estas tabelas:

cliente
----------------
ID_CLIENTE
NOME

E:

pedido
----------------
ID_PEDIDO
ID_CLIENTE
DATA_PEDIDO
VALOR_TOTAL

Podemos começar calculando o total comprado por cliente:

SELECT
  P.ID_CLIENTE,
  SUM(P.VALOR_TOTAL) AS TOTAL_COMPRADO
FROM pedido P
GROUP BY
  P.ID_CLIENTE;

Imagine o resultado:

ID_CLIENTETOTAL_COMPRADO
1850,00
22.300,00
31.200,00
4430,00

Até aqui, nenhuma complicação.

Mas ainda não respondemos à pergunta.

Precisamos descobrir a média desses totais.


Uma solução utilizando subquery

Podemos utilizar nossa primeira consulta como uma tabela intermediária.

Por exemplo:

SELECT
  AVG(V.TOTAL_COMPRADO) AS MEDIA_COMPRAS
FROM (
  SELECT
    P.ID_CLIENTE,
    SUM(P.VALOR_TOTAL) AS TOTAL_COMPRADO
  FROM pedido P
  GROUP BY
    P.ID_CLIENTE
) V;

Observe a estrutura.

Temos uma consulta dentro do FROM:

SELECT
  P.ID_CLIENTE,
  SUM(P.VALOR_TOTAL) AS TOTAL_COMPRADO
FROM pedido P
GROUP BY
  P.ID_CLIENTE

Ela produz algo parecido com:

CLIENTE 1 → 850
CLIENTE 2 → 2300
CLIENTE 3 → 1200
CLIENTE 4 → 430

A consulta externa pega esses resultados e calcula:

AVG(V.TOTAL_COMPRADO)

Funciona.

Mas ainda precisamos utilizar essa média para encontrar os clientes acima dela.

Conforme adicionamos etapas, a consulta começa a ficar mais difícil de ler.

E é aqui que a CTE começa a ficar interessante.


O que é uma CTE?

CTE significa:

Common Table Expression.

Mas você não precisa decorar o nome para entender o conceito.

Pense nela como uma forma de:

dar um nome para uma etapa da consulta.

Em vez de colocar uma consulta dentro de outra e depois outra…

podemos separar partes do raciocínio.

Por exemplo:

WITH total_cliente AS (
  SELECT
    P.ID_CLIENTE,
    SUM(P.VALOR_TOTAL) AS TOTAL_COMPRADO
  FROM pedido P
  GROUP BY
    P.ID_CLIENTE
)
SELECT
  *
FROM total_cliente;

Leia assim:

Primeiro crie o resultado total_cliente.

Depois trabalhe com esse resultado.

Isso combina muito bem com a forma como pensamos o problema.


Transformando o raciocínio em etapas

Nossa pergunta original era:

Quais clientes gastaram acima da média de gastos dos clientes?

Podemos dividir assim:

Etapa 1

Calcular quanto cada cliente gastou.

Etapa 2

Calcular a média desses valores.

Etapa 3

Comparar cada cliente com essa média.

Agora podemos representar essas etapas com CTEs.

WITH total_cliente AS (
  SELECT
    P.ID_CLIENTE,
    SUM(P.VALOR_TOTAL) AS TOTAL_COMPRADO
  FROM pedido P
  GROUP BY
    P.ID_CLIENTE
),
media_cliente AS (
  SELECT
    AVG(TOTAL_COMPRADO) AS MEDIA_COMPRAS
  FROM total_cliente
)
SELECT
  C.ID_CLIENTE,
  C.NOME,
  TC.TOTAL_COMPRADO
FROM total_cliente TC
INNER JOIN cliente C
  ON TC.ID_CLIENTE = C.ID_CLIENTE
CROSS JOIN media_cliente MC
WHERE TC.TOTAL_COMPRADO > MC.MEDIA_COMPRAS
ORDER BY
  TC.TOTAL_COMPRADO DESC;

Pode parecer maior.

Mas observe algo importante.

Conseguimos enxergar as etapas.


Leia a consulta como uma história

Primeiro:

total_cliente

responde:

Quanto cada cliente gastou?

Depois:

media_cliente

responde:

Qual é a média desses totais?

Finalmente:

SELECT

responde:

Quais clientes ficaram acima dessa média?

Essa é uma das grandes vantagens de utilizar CTE em determinados cenários.

Ela pode deixar visível o raciocínio da análise.


Então CTE é melhor?

Não necessariamente.

Vamos pegar um problema mais simples:

Quais produtos possuem preço acima da média do catálogo?

Podemos escrever:

SELECT
  ID_PRODUTO,
  NOME,
  PRECO
FROM produto
WHERE PRECO > (
  SELECT
    AVG(PRECO)
  FROM produto
);

Essa consulta é pequena.

Clara.

Direta.

Agora imagine transformá-la obrigatoriamente em CTE apenas porque alguém disse:

“CTE é melhor.”

Poderíamos fazer:

WITH media_preco AS (
  SELECT
    AVG(PRECO) AS MEDIA
  FROM produto
)
SELECT
  P.ID_PRODUTO,
  P.NOME,
  P.PRECO
FROM produto P
CROSS JOIN media_preco M
WHERE P.PRECO > M.MEDIA;

Funciona.

Mas ficou necessariamente melhor?

Não.

Para esse problema específico, a subquery representa a ideia muito bem:

preço maior que…

a média dos preços.

Simples.


A primeira pergunta não deveria ser “CTE ou subquery?”

Eu começaria com:

Quantas etapas existem no meu raciocínio?

Se existe apenas uma informação intermediária pequena e fácil de entender, uma subquery pode ser suficiente.

Se você percebe que está construindo uma análise com várias etapas, uma CTE pode ajudar a organizar melhor a consulta.

Essa não é uma regra absoluta.

É um critério de clareza.


Pense em blocos de raciocínio

Imagine outra pergunta:

Quais vendedores faturaram acima da média da própria região?

Podemos enxergar alguns blocos:

Bloco 1

Calcular faturamento por vendedor.

Bloco 2

Calcular média por região.

Bloco 3

Comparar vendedor com sua região.

Quando o problema começa a possuir blocos bem definidos, nomeá-los pode tornar a consulta muito mais compreensível.

Por exemplo:

faturamento_vendedor
media_regiao
resultado_final

Esses nomes ajudam quem está lendo a entender por que aquela parte da consulta existe.


CTE pode melhorar a leitura

Compare mentalmente:

FROM (
  SELECT …
  FROM (
    SELECT …
  ) X
) Y

com:

WITH etapa_1 AS (
  …
),
etapa_2 AS (
  …
)
SELECT …

Em consultas maiores, a segunda estrutura muitas vezes permite acompanhar melhor o raciocínio.

Principalmente quando os nomes são bons.

Mas existe um detalhe.


Dar nomes ruins também cria confusão

Imagine isto:

WITH cte1 AS (
  …
),
cte2 AS (
  …
),
cte3 AS (
  …
)

Tecnicamente funciona.

Mas os nomes não explicam nada.

Prefira algo que represente a informação produzida:

WITH vendas_cliente AS (
  …
),
media_vendas AS (
  …
)

Agora, antes mesmo de ler o SQL interno, já conseguimos entender parte da lógica.

Nomear bem uma CTE é parte da clareza da consulta.


E performance?

Aqui existe uma armadilha comum.

Você pode ouvir:

“CTE é mais rápida.”

Ou:

“Subquery é mais rápida.”

Não trate nenhuma dessas frases como regra universal.

A forma como uma CTE ou subquery será executada depende do banco, da versão, da consulta, dos índices, das estatísticas e das decisões do otimizador.

Em muitos casos, duas consultas escritas de formas diferentes podem acabar com planos de execução muito parecidos.

Em outros, não.

Por isso:

não escolha CTE ou subquery baseado apenas em uma regra genérica de performance.

Primeiro escreva uma solução correta e clara.

Quando performance realmente for um problema, analise o plano de execução e teste.


Legibilidade também importa profissionalmente

Existe algo que quem está começando às vezes esquece.

A consulta não será necessariamente lida apenas por você.

Pode ser lida:

  • por outro analista;
  • por um engenheiro;
  • por alguém revisando seu código;
  • por você mesmo seis meses depois.

Uma query que funciona, mas que ninguém consegue entender, cria um problema de manutenção.

Por isso clareza não é apenas estética.

Também é qualidade.


Quando eu consideraria uma subquery?

De forma prática, eu começaria considerando uma subquery quando a informação intermediária for simples.

Por exemplo:

Produtos acima da média.

WHERE PRECO > (
  SELECT AVG(PRECO)
  FROM produto
)

A leitura é natural.

Outro exemplo:

Pedido com maior valor.

WHERE VALOR_TOTAL = (
  SELECT MAX(VALOR_TOTAL)
  FROM pedido
)

Novamente, simples.


Quando eu consideraria uma CTE?

Quando percebo que estou pensando:

Primeiro preciso fazer isso.

Depois aquilo.

Com esse resultado preciso calcular outra coisa.

Finalmente preciso juntar tudo.

Nesse cenário, uma CTE pode ajudar bastante.

Especialmente quando cada etapa possui um significado claro.

Por exemplo:

vendas_cliente

clientes_ativos

media_cliente

resultado_final

A própria estrutura começa a documentar o raciocínio.


Existe outra vantagem importante

Uma CTE também pode evitar que uma lógica intermediária fique escondida no meio de uma consulta grande.

Imagine que você calculou:

faturamento por cliente nos últimos seis meses.

Essa informação é importante para toda a análise.

Dar um nome como:

faturamento_cliente

pode tornar a consulta muito mais fácil de entender.

Você passa a trabalhar com um conceito.

Não apenas com um bloco de código.


Mas não transforme toda consulta em CTE

Depois que alguém aprende CTE, existe uma fase divertida.

Tudo vira CTE.

Consulta simples?

CTE.

Filtro?

CTE.

Agregação?

Outra CTE.

De repente temos:

cte_1
cte_2
cte_3
cte_4
cte_5
cte_final

para resolver algo que poderia ter sido escrito claramente em dez linhas.

Mais estrutura não significa automaticamente mais clareza.

Às vezes, simplificar é melhor.


Um bom critério: consigo explicar a consulta?

Depois de escrever, tente explicar sua solução em português.

Por exemplo:

Primeiro calculei o total comprado por cliente.

Depois calculei a média desses totais.

Finalmente filtrei os clientes acima da média.

Se sua CTE acompanha exatamente essa explicação, provavelmente a estrutura está ajudando.

Agora, se você precisa dizer:

Criei a CTE 1 porque precisava da CTE 2, que alimenta a CTE 3…

Talvez seja hora de revisar.


E no portfólio?

Esse assunto também importa quando você monta projetos.

Imagine alguém avaliando duas consultas.

A primeira funciona, mas possui várias subqueries aninhadas difíceis de acompanhar.

A segunda deixa claramente visíveis as etapas da análise.

Mesmo que as duas produzam o mesmo resultado, a segunda pode comunicar melhor seu raciocínio.

Isso não significa:

“Use CTE para impressionar.”

Muito pelo contrário.

Use a estrutura que deixa sua decisão mais fácil de entender.

Um bom portfólio não deveria mostrar quantos recursos avançados você conhece.

Deveria mostrar como você pensa.


Um exercício

Imagine a pergunta:

Quais clientes possuem ticket médio acima do ticket médio geral?

Antes de escrever SQL, tente separar o problema.

Talvez você precise descobrir:

  1. o ticket médio de cada cliente;
  2. o ticket médio geral;
  3. quais clientes estão acima dessa referência.

Agora pergunte:

Eu resolveria isso com subquery?

Ou separar em CTEs deixaria meu raciocínio mais claro?

Tente escrever das duas formas.

Esse exercício é muito melhor do que simplesmente memorizar:

“CTE serve para X.”

Porque você começa a perceber as diferenças na prática.


Outro exercício

Pegue nosso exemplo simples:

Produtos com preço acima da média.

Primeiro escreva com subquery:

SELECT
  ID_PRODUTO,
  NOME,
  PRECO
FROM produto
WHERE PRECO > (
  SELECT AVG(PRECO)
  FROM produto
);

Depois escreva utilizando CTE.

Execute as duas.

Compare.

Pergunte:

  • qual ficou mais fácil de ler?
  • qual representa melhor a pergunta?
  • existe alguma diferença no plano de execução no seu banco?
  • alguma delas ficou desnecessariamente complicada?

Esse tipo de comparação desenvolve muito mais maturidade do que decorar regras.


Então qual devo usar?

Se você esperava uma tabela dizendo:

SituaçãoUse
XCTE
YSubquery

eu prefiro não simplificar dessa forma.

Em vez disso, use algumas perguntas:

  • Existe apenas uma informação intermediária simples?
  • Existem várias etapas de transformação?
  • A consulta está ficando difícil de acompanhar?
  • Nomear os resultados intermediários melhora a leitura?
  • A mesma lógica precisa aparecer de forma clara em outras partes da consulta?
  • Qual solução representa melhor o raciocínio?
  • Existe um problema real de performance que precisa ser medido?

Essas perguntas são muito mais úteis do que uma regra decorada.


O mais importante continua acontecendo antes do SQL

No artigo anterior, vimos que uma subquery aparece naturalmente quando existe uma pergunta dentro de outra pergunta.

Agora demos mais um passo.

Quando existem várias etapas de raciocínio, uma CTE pode ajudar a torná-las explícitas.

Então perceba a evolução:

Não começamos com:

WITH.

Nem com:

SELECT.

Começamos com:

Qual problema precisamos resolver?

Depois organizamos as etapas.

Só então escolhemos a estrutura SQL.


Conclusão

CTE e subquery não estão competindo para descobrir qual é “melhor”.

São ferramentas.

Em uma consulta simples, uma subquery pode representar perfeitamente o problema.

Em uma análise com várias etapas, uma CTE pode deixar o raciocínio muito mais organizado.

E em muitos casos existirão várias soluções corretas.

A maturidade não está em decorar:

“Sempre use CTE.”

Ou:

“Subquery é mais rápida.”

Está em conseguir olhar para o problema e perguntar:

Qual estrutura deixa minha intenção mais clara?

Porque escrever SQL não é apenas fazer uma consulta funcionar.

É construir uma solução que você — e outras pessoas — consigam entender.

Próximo passo

Na Coleção A Arte da Query, você encontra cenários em que precisa tomar exatamente esse tipo de decisão.

Você recebe o contexto e o problema.

Mas precisa descobrir o caminho.

Às vezes um JOIN resolve.

Às vezes uma subquery faz sentido.

Em outras situações, dividir a solução em etapas deixa tudo mais claro.

Essa é a diferença entre apenas conhecer os recursos do SQL e desenvolver a capacidade de escolher quando utilizá-los.

E essa capacidade só aparece quando você pratica SQL com propósito.

Link: https://artedaquery.com.br/

Tags: |

0 Comentários

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *

Solicitar exportação de dados

Use este formulário para solicitar uma cópia de seus dados neste site.

Solicitar a remoção de dados

Use este formulário para solicitar a remoção de seus dados neste site.

Solicitar retificação de dados

Use este formulário para solicitar a retificação de seus dados neste site. Aqui você pode corrigir ou atualizar seus dados, por exemplo.

Solicitar cancelamento de inscrição

Use este formulário para solicitar a cancelamento da inscrição do seu e-mail em nossas listas de e-mail.