SQL GROUP BY

Com COUNT(*) a Ana descobriu que a Loja Aurora tem 30 clientes. A pergunta seguinte da gerente foi: "E quantos de cada cidade?". Ela poderia rodar um COUNT com WHERE cidade = 'Teresina', outro com 'Fortaleza', outro com 'São Paulo'… e torcer para não esquecer nenhuma cidade. O GROUP BY ("agrupar por") faz tudo de uma vez: separa as linhas em grupos e calcula as agregadas para cada grupo. É aqui que as funções agregadas mostram para que vieram.

Você vai aprender
  • Como o GROUP BY separa as linhas em grupos e devolve uma linha por grupo.
  • Juntar várias agregadas por grupo: o relatório por categoria.
  • Ordenar os grupos e ficar com os primeiros (ORDER BY, LIMIT).
  • Filtrar as linhas antes de agrupar com WHERE, e a ordem lógica de uma consulta.
  • Agrupar por uma conta (o ano do pedido) e o grupo do NULL.
  • A regra das colunas do SELECT, e os erros reais quando ela é quebrada.

Antes desta aula, veja O que são Funções Agregadas e COUNT().


A ideia: separar as fichas em pilhas

Imagine as 30 fichas de cliente da loja espalhadas na mesa. Para saber quantos clientes há em cada cidade, você faria assim:

  1. Separa as fichas em pilhas, uma por cidade: a pilha de Teresina, a de Fortaleza, a de São Paulo…
  2. Conta as fichas de cada pilha.
  3. Anota numa folha uma linha por pilha: a cidade e o total.
pilha "Teresina"   → José, Maria Clara, Conceição, Antônio… (10 fichas)
pilha "São Paulo"  → Ana Beatriz, Rafael, Marcos Vinícius…   (7 fichas)
pilha "Fortaleza"  → Carlos Eduardo, Fernanda, Patrícia…     (7 fichas)
pilha "Recife"     → Juliana, Bruno                          (2 fichas)
… e uma pilha para cada uma das outras quatro cidades

O passo 1 é o GROUP BY cidade. Os passos 2 e 3 são o COUNT(*) calculado em cada pilha. Sem GROUP BY, as agregadas tratam a tabela inteira como uma pilha só; com ele, cada pilha ganha a sua conta.


1. O primeiro GROUP BY: clientes por cidade

SELECT cidade, COUNT(*) AS clientes
FROM clientes
GROUP BY cidade;
SELECT cidade,
A coluna do grupo aparece no resultado, para você saber de qual pilha é cada número.
COUNT(*) AS clientes
A agregada, agora calculada uma vez por grupo.
FROM clientes
As 30 linhas de onde saem as pilhas.
GROUP BY cidade
"Agrupe por cidade": linhas com a mesma cidade vão para o mesmo grupo. Ele vem depois do FROM (e do WHERE, quando houver).

Trinta linhas entraram, oito saíram: uma por cidade diferente. Somando a coluna clientes (1 + 7 + 1 + 1 + 1 + 2 + 7 + 10), dá 30 de novo: cada cliente caiu em exatamente uma pilha.

Repare que as cidades saíram em ordem alfabética. Isso é efeito do jeito como o SQLite monta os grupos, não uma promessa: o padrão SQL não garante ordem nenhuma sem ORDER BY, e outros bancos (ou o próprio SQLite, com outra consulta) podem devolver em outra ordem. Quando a ordem importa, peça (seção 3).

Experimente: a consulta tenta mostrar quantos pedidos cada funcionário atendeu, mas devolve uma linha só, com o total geral. Falta a linha que separa os pedidos em pilhas, uma por funcionario_id.

SELECT funcionario_id, COUNT(*) AS pedidos
FROM pedidos;
SELECT funcionario_id, COUNT(*) AS pedidos
FROM pedidos
GROUP BY funcionario_id;

Os seis vendedores atenderam 15 pedidos cada um. A gerente distribuiu o trabalho direitinho.


2. Várias agregadas por grupo: o relatório por categoria

Tudo o que você aprendeu nas aulas anteriores funciona por grupo. O relatório de produtos, categoria por categoria:

SELECT categoria_id,
       COUNT(*)             AS produtos,
       MIN(preco)           AS mais_barato,
       MAX(preco)           AS mais_caro,
       ROUND(AVG(preco), 2) AS preco_medio,
       SUM(estoque)         AS unidades
FROM produtos
GROUP BY categoria_id;

Oito categorias, oito linhas. Em cada linha, as cinco agregadas olharam só os produtos daquela categoria. A Papelaria (1) tem o produto mais barato da loja e o maior estoque; Casa e Cozinha (3) tem o mais caro. Para ver o nome da categoria em vez do número, é preciso juntar duas tabelas, o que você aprende em INNER JOIN.

O total de cada pedido

A tabela itens_pedido tem várias linhas por pedido. Agrupando por pedido_id, cada pedido vira uma pilha, e o SUM soma os itens de cada um. Os cinco pedidos de maior valor em produtos:

SELECT pedido_id,
       ROUND(SUM(quantidade * preco_unitario), 2) AS total
FROM itens_pedido
GROUP BY pedido_id
ORDER BY total DESC
LIMIT 5;

3. Ordenar os grupos

A gerente quer as cidades da que tem mais clientes para a que tem menos. O ORDER BY vem depois do GROUP BY e pode usar a agregada (ou o apelido dela):

SELECT cidade, COUNT(*) AS clientes
FROM clientes
GROUP BY cidade
ORDER BY clientes DESC, cidade;

Teresina na frente, com 10. Fortaleza e São Paulo empatam com 7, e o segundo critério, cidade, desempata em ordem alfabética. Para a "cidade campeã", ponha LIMIT 1 no fim.


4. WHERE antes, GROUP BY depois

Agora só os clientes do Piauí, por cidade. O filtro de linhas é o WHERE de sempre, e ele vem antes do GROUP BY:

SELECT cidade, COUNT(*) AS clientes
FROM clientes
WHERE estado = 'PI'
GROUP BY cidade
ORDER BY clientes DESC, cidade;

Os clientes de fora do Piauí foram descartados antes de formar as pilhas, então São Paulo, Fortaleza e Recife nem aparecem.

A ordem lógica de uma consulta

Você escreve a consulta numa ordem, mas o banco "pensa" nela em outra. Entender essa ordem explica quase todos os erros desta parte do guia:

PassoCláusulaO que aconteceNesta consulta
1FROMPega as linhas da tabela30 clientes
2WHEREDescarta linhas, uma por umaFicam os 14 do Piauí
3GROUP BYSepara as linhas em grupos5 pilhas, uma por cidade
4HAVINGDescarta grupos inteiros (próxima aula)(não tem)
5SELECTCalcula as agregadas e as colunas de cada grupo; dá os apelidoscidade, COUNT(*)
6ORDER BYOrdena as linhas do resultadoMais clientes primeiro
7LIMITCorta o resultado(não tem)

Duas consequências importantes:

  • O WHERE roda antes dos grupos existirem. Por isso ele não pode usar COUNT, SUM… (dá misuse of aggregate function). Para filtrar pelo resultado de uma agregada, como "só as cidades com mais de 5 clientes", existe o HAVING, o assunto da aula HAVING.
  • A ordem em que você escreve é fixa: SELECT … FROM … WHERE … GROUP BY … HAVING … ORDER BY … LIMIT. Trocar duas cláusulas de lugar é erro de sintaxe (veja "Quando dá errado").

Pela tabela, o apelido só nasce no passo 5, depois do GROUP BY. Mesmo assim, o SQLite aceita apelidos no GROUP BY e no HAVING, como você vai ver na seção 5. É uma gentileza dele, e nem todo banco faz o mesmo.


5. Agrupar por uma conta: pedidos por ano

O GROUP BY aceita uma expressão, não só uma coluna. A data do pedido é um texto como '2024-01-05'; os 4 primeiros caracteres são o ano. A função substr(texto, início, tamanho) (aula Funções de Texto) recorta esse pedaço:

SELECT substr(data, 1, 4) AS ano,
       COUNT(*)           AS pedidos,
       SUM(frete)         AS frete
FROM pedidos
GROUP BY ano;

O GROUP BY ano usou o apelido da coluna; daria no mesmo escrever GROUP BY substr(data, 1, 4). 2026 tem menos pedidos porque o ano ainda não acabou: os dados vão até outubro.

O NULL vira um grupo

Os dois fornecedores estrangeiros têm estado NULL. No agrupamento, os NULL são considerados iguais entre si e formam um grupo só:

SELECT estado, COUNT(*) AS fornecedores
FROM fornecedores
GROUP BY estado;

A primeira linha é o grupo dos "sem estado", com 2 fornecedores. É diferente do WHERE estado = NULL, que nunca é verdadeiro: o GROUP BY junta os desconhecidos numa pilha só, para nada se perder.

Experimente: a consulta agrupa os clientes por cidade. Mude para agrupar por estado: quantos clientes há em cada UF?

SELECT cidade, COUNT(*) AS clientes
FROM clientes
GROUP BY cidade;
SELECT estado, COUNT(*) AS clientes
FROM clientes
GROUP BY estado;

6. A regra das colunas do SELECT

Depois do agrupamento, cada grupo vira uma linha. Então cada coluna do SELECT precisa ter um valor só por grupo. Pela regra do SQL padrão, isso só é garantido para dois tipos de coluna:

  • as que estão no GROUP BY (todas as linhas da pilha de Teresina têm cidade = Teresina);
  • as que estão dentro de uma agregada (COUNT(*), MAX(preco)…).

E o nome do cliente, numa consulta agrupada por cidade? A pilha de Teresina tem 10 nomes. Veja o que o SQLite faz:

SELECT nome, cidade
FROM clientes
GROUP BY cidade;

Ele não reclama e mostra um nome de cada cidade, sem critério que você controle. É a coluna nua, a mesma da aula MAX(): uma extensão do SQLite. PostgreSQL, MySQL (no modo padrão) e SQL Server recusam essa consulta com erro. Siga a regra do padrão: no SELECT de uma consulta agrupada, só colunas do GROUP BY ou agregadas.


7. Formas diferentes de escrever o agrupamento

SELECT cidade, COUNT(*) AS clientes
FROM clientes
GROUP BY 1
ORDER BY 2 DESC
LIMIT 3;

GROUP BY 1 quer dizer "agrupe pela 1ª coluna do SELECT", e ORDER BY 2, "ordene pela 2ª". É curto, mas quebra fácil: se alguém acrescentar uma coluna no começo do SELECT, o número passa a apontar para outra coisa.

E o GROUP BY sem nenhuma agregada? Ele dá o mesmo que um SELECT DISTINCT (aula SELECT DISTINCT):

SELECT cidade
FROM clientes
GROUP BY cidade;
FormaExemploQuando usar
Pelo nome da colunaGROUP BY cidadeSempre que puder: é a mais clara e funciona em todo banco
Pela expressãoGROUP BY substr(data, 1, 4)Quando o grupo é uma conta; funciona em todo banco
Pelo apelidoGROUP BY anoMais legível com contas longas; aceito no SQLite e no PostgreSQL, mas não no SQL Server
Pela posiçãoGROUP BY 1Só em consultas rápidas, de teste
Só valores diferentesSELECT DISTINCT cidadeQuando não precisa de agregada nenhuma

Quando dá errado

Esqueceu o GROUP BY
SELECT cidade, COUNT(*) AS clientes
FROM clientes;

O que aconteceu: nenhum erro, e uma linha só: "Teresina, 30". Parece dizer que há 30 clientes em Teresina, mas o 30 é o total da loja. Sem GROUP BY, a tabela inteira é uma pilha só; e a cidade, que ficou nua, mostra uma cidade qualquer.

Como consertar: acrescente GROUP BY cidade. Desconfie sempre de uma consulta "por cidade" que devolve uma linha só.

WHERE depois do GROUP BY
SELECT cidade, COUNT(*)
FROM clientes
GROUP BY cidade
WHERE estado = 'PI';

A mensagem: near "WHERE": syntax error, "erro de sintaxe perto de WHERE". A ordem das cláusulas é fixa, e o WHERE vem antes do GROUP BY.

Como consertar: FROM clientes WHERE estado = 'PI' GROUP BY cidade. Se a intenção era filtrar grupos, a palavra certa depois do GROUP BY é HAVING.

ORDER BY antes do GROUP BY
SELECT cidade, COUNT(*)
FROM clientes
ORDER BY cidade
GROUP BY cidade;

A mensagem: near "GROUP": syntax error. O ORDER BY é das últimas cláusulas; ele ordena o resultado já agrupado.

Agregada dentro do GROUP BY
SELECT cidade, COUNT(*)
FROM clientes
GROUP BY COUNT(*);

A mensagem: aggregate functions are not allowed in the GROUP BY clause, "funções agregadas não são permitidas no GROUP BY". Faz sentido pela ordem lógica: para contar, os grupos já têm de existir; não dá para formar os grupos pelo resultado da conta.

Uma posição que não existe
SELECT cidade, COUNT(*)
FROM clientes
GROUP BY 3;

A mensagem: 1st GROUP BY term out of range - should be between 1 and 2, "o 1º termo do GROUP BY está fora do intervalo; deveria ser entre 1 e 2". O SELECT só tem duas colunas.

Uma agregada dentro de outra

"Qual é o maior número de clientes numa cidade?":

SELECT MAX(COUNT(*))
FROM clientes
GROUP BY cidade;

A mensagem: misuse of aggregate function COUNT(). Uma agregada não pode ficar dentro de outra na mesma consulta.

Como consertar: ordene os grupos e fique com o primeiro: … GROUP BY cidade ORDER BY COUNT(*) DESC LIMIT 1. (Outra saída, com subconsulta, fica para a aula Subconsultas.)

Nos outros bancos
  • Coluna nua: PostgreSQL, MySQL (com o ONLY_FULL_GROUP_BY, ligado por padrão) e SQL Server recusam colunas no SELECT que não estão no GROUP BY nem numa agregada. O PostgreSQL e o MySQL abrem uma exceção quando a coluna depende da chave primária agrupada. Os textos dos erros estão na aula MAX().
  • Apelido no GROUP BY: o PostgreSQL aceita (GROUP BY ano); o SQL Server não, porque lá os apelidos só existem a partir do SELECT. Repita a expressão: GROUP BY SUBSTRING(data, 1, 4).
  • Ordem: a documentação do SQL Server avisa que o GROUP BY não garante a ordem do resultado. Use ORDER BY em todo banco.
SELECT SUBSTRING(data, 1, 4) AS ano, COUNT(*) AS pedidos
FROM pedidos
GROUP BY SUBSTRING(data, 1, 4);

Referência rápida

EscritaO que faz
GROUP BY colunaUma linha por valor diferente da coluna; as agregadas são calculadas por grupo
GROUP BY expressãoAgrupa pelo resultado da conta (ex.: o ano da data)
WHERE … GROUP BY …Filtra as linhas antes de agrupar
GROUP BY … ORDER BY COUNT(*) DESCOrdena os grupos pela agregada
NULL na coluna do grupoOs NULL formam um grupo só
Ordem de escritaSELECT → FROM → WHERE → GROUP BY → HAVING → ORDER BY → LIMIT
Ordem lógicaFROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY → LIMIT
Mensagem do SQLiteQuer dizer
near "WHERE": syntax errorWHERE escrito depois do GROUP BY
near "GROUP": syntax errorGROUP BY escrito depois do ORDER BY
aggregate functions are not allowed in the GROUP BY clauseAgregada dentro do GROUP BY
1st GROUP BY term out of range - should be between 1 and 2Posição maior que o número de colunas do SELECT
misuse of aggregate function COUNT()Agregada dentro de outra, ou no WHERE

Exercícios

1. Clientes VIP e comuns

Complete para contar quantos clientes há em cada valor de vip (0 e 1):

SELECT vip, COUNT(*) FROM clientes vip;

2. Unidades por produto

Complete para somar as unidades vendidas de cada produto:

SELECT produto_id, (quantidade) FROM itens_pedido GROUP BY ;

3. A tabela clientes tem 30 linhas e 4 estados diferentes. Quantas linhas tem SELECT estado, COUNT(*) FROM clientes GROUP BY estado;?

4. Na ordem lógica, o que acontece primeiro?

5. Numa consulta com GROUP BY cidade, qual destas colunas segue a regra do SQL padrão no SELECT?

6. Dois fornecedores têm estado NULL. Em GROUP BY estado, eles…

Missão final: o valor do estoque por categoria

Missão: a consulta mostra o valor em estoque de cada produto (45 linhas). A gerente quer uma linha por categoria: o categoria_id e o valor total do estoque dela (valor_estoque, a soma de preço × estoque), arredondado para centavos.

SELECT categoria_id, preco * estoque AS valor_estoque
FROM produtos;
SELECT categoria_id,
       ROUND(SUM(preco * estoque), 2) AS valor_estoque
FROM produtos
GROUP BY categoria_id;

Resumo

  • GROUP BY coluna separa as linhas em grupos (as pilhas) e devolve uma linha por grupo; as agregadas são calculadas em cada grupo.
  • O WHERE filtra as linhas antes de agrupar. A ordem lógica é FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY → LIMIT.
  • Sem ORDER BY, a ordem dos grupos não é garantida. Ordene pela agregada para fazer rankings.
  • Pode agrupar por uma expressão (substr(data, 1, 4)). Os NULL formam um grupo só.
  • No SELECT, só colunas do GROUP BY ou agregadas. O SQLite aceita colunas nuas, mas o valor delas é de uma linha qualquer, e os outros bancos dão erro.
  • Sem GROUP BY, a tabela inteira é um grupo só: "Teresina, 30" é o total da loja, não de Teresina.