Dados · Júnior

SQL além do básico: agregações, GROUP BY e subqueries

Saber fazer SELECT * FROM tabela é o básico do básico. O que separa quem está começando de quem realmente entrega valor com dados é a capacidade de responder perguntas de negócio: "qual o ticket médio?", "quais clientes compraram mais?". Para isso, você precisa de agregações, agrupamentos e subconsultas.

Funções de agregação: resumindo muitas linhas em um número

As funções de agregação pegam várias linhas e devolvem um único valor. As cinco fundamentais são:

  • COUNT — conta linhas. COUNT(*) conta tudo; COUNT(coluna) ignora nulos.
  • SUM — soma valores numéricos.
  • AVG — calcula a média.
  • MIN / MAX — o menor e o maior valor.

Sozinhas, elas resumem a tabela inteira. SELECT AVG(valor) FROM pedidos devolve o ticket médio de todos os pedidos.

GROUP BY: agregar por categoria

Quase nunca queremos o resumo de tudo — queremos por cliente, por mês, por categoria. O GROUP BY divide as linhas em grupos e aplica a agregação a cada um separadamente.

SELECT cliente_id, COUNT(*) AS total_pedidos, SUM(valor) AS gasto
FROM pedidos
GROUP BY cliente_id;

A regra que mais gera erro: toda coluna no SELECT que não está dentro de uma função de agregação precisa estar no GROUP BY.

WHERE vs HAVING: a diferença que sempre cai

Ambos filtram, mas em momentos diferentes do processamento. Pense numa linha de produção:

AspectoWHEREHAVING
Quando ageAntes de agruparDepois de agrupar
FiltraLinhas individuaisGrupos (resultados agregados)
Pode usar SUM/COUNT?NãoSim

Por exemplo: "clientes que gastaram mais de R$ 1000". O filtro é sobre a soma, então usa-se HAVING:

SELECT cliente_id, SUM(valor) AS gasto
FROM pedidos
WHERE valor > 0
GROUP BY cliente_id
HAVING SUM(valor) > 1000;

Subqueries: consultas dentro de consultas

Uma subquery é um SELECT aninhado dentro de outro. Útil quando o filtro depende de um cálculo. "Pedidos acima da média":

SELECT id, valor
FROM pedidos
WHERE valor > (SELECT AVG(valor) FROM pedidos);

A subquery interna roda primeiro, calcula a média, e o resultado vira o critério da consulta externa.

CTE: organizando com WITH

Quando a lógica fica complexa, subqueries aninhadas viram um nó. A CTE (Common Table Expression), com a cláusula WITH, dá um nome a um resultado intermediário, deixando a consulta legível como uma receita passo a passo.

WITH gasto_por_cliente AS (
  SELECT cliente_id, SUM(valor) AS gasto
  FROM pedidos
  GROUP BY cliente_id
)
SELECT * FROM gasto_por_cliente
WHERE gasto > 1000;
A maioria das perguntas de negócio é uma agregação com um filtro. Quem entende a ordem em que o SQL processa — filtra, agrupa, filtra de novo — escreve consultas corretas de primeira em vez de chutar.

É comum em entrevista pedir para traduzir uma pergunta em palavras ("os 5 produtos mais vendidos do mês") em SQL, e o ponto avaliado quase sempre é justamente o GROUP BY com HAVING ou uma subquery bem colocada. No dia a dia isso aparece em todo relatório e dashboard que você precisa alimentar.

Conclusão

Agregações, GROUP BY, a distinção WHERE/HAVING, subqueries e CTEs formam o ferramental que transforma tabelas brutas em respostas. Tudo isso assume que você entende como as tabelas se relacionam — se ainda tem dúvidas aí, vale revisar modelagem relacional antes de seguir.

Knowing how to write SELECT * FROM table is the very basics. What separates people who are just starting out from those who really deliver value with data is the ability to answer business questions: "what's the average ticket?", "which customers bought the most?". For that, you need aggregations, grouping and subqueries.

Aggregate functions: boiling many rows down to one number

Aggregate functions take many rows and return a single value. The five fundamental ones are:

  • COUNT — counts rows. COUNT(*) counts everything; COUNT(column) ignores nulls.
  • SUM — adds up numeric values.
  • AVG — computes the average.
  • MIN / MAX — the smallest and largest value.

On their own, they summarize the entire table. SELECT AVG(valor) FROM pedidos returns the average ticket across all orders.

GROUP BY: aggregating by category

We almost never want a summary of everything — we want it per customer, per month, per category. GROUP BY splits the rows into groups and applies the aggregation to each one separately.

SELECT cliente_id, COUNT(*) AS total_pedidos, SUM(valor) AS gasto
FROM pedidos
GROUP BY cliente_id;

The rule that causes the most errors: every column in the SELECT that is not inside an aggregate function must be in the GROUP BY.

WHERE vs HAVING: the difference that always comes up

Both filter, but at different moments of processing. Think of a production line:

AspectWHEREHAVING
When it actsBefore groupingAfter grouping
FiltersIndividual rowsGroups (aggregated results)
Can use SUM/COUNT?NoYes

For example: "customers who spent more than $1,000". The filter is on the sum, so you use HAVING:

SELECT cliente_id, SUM(valor) AS gasto
FROM pedidos
WHERE valor > 0
GROUP BY cliente_id
HAVING SUM(valor) > 1000;

Subqueries: queries inside queries

A subquery is a SELECT nested inside another one. Useful when the filter depends on a calculation. "Orders above the average":

SELECT id, valor
FROM pedidos
WHERE valor > (SELECT AVG(valor) FROM pedidos);

The inner subquery runs first, computes the average, and the result becomes the criterion of the outer query.

CTE: getting organized with WITH

When the logic gets complex, nested subqueries turn into a tangle. The CTE (Common Table Expression), with the WITH clause, gives a name to an intermediate result, making the query read like a step-by-step recipe.

WITH gasto_por_cliente AS (
  SELECT cliente_id, SUM(valor) AS gasto
  FROM pedidos
  GROUP BY cliente_id
)
SELECT * FROM gasto_por_cliente
WHERE gasto > 1000;
Most business questions are an aggregation with a filter. Whoever understands the order in which SQL processes things — filter, group, filter again — writes correct queries on the first try instead of guessing.

In interviews it's common to be asked to translate a question in plain words ("the top 5 best-selling products of the month") into SQL, and what's being evaluated is almost always the GROUP BY with HAVING or a well-placed subquery. Day to day, this shows up in every report and dashboard you need to feed.

Conclusion

Aggregations, GROUP BY, the WHERE/HAVING distinction, subqueries and CTEs make up the toolkit that turns raw tables into answers. All of this assumes you understand how tables relate to each other — if you still have doubts there, it's worth reviewing relational modeling before moving on.

Leia também

Voltar à trilhaBack to the track