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.
- Como o
GROUP BYsepara 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:
- Separa as fichas em pilhas, uma por cidade: a pilha de Teresina, a de Fortaleza, a de São Paulo…
- Conta as fichas de cada pilha.
- 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 doWHERE, 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:
| Passo | Cláusula | O que acontece | Nesta consulta |
|---|---|---|---|
| 1 | FROM | Pega as linhas da tabela | 30 clientes |
| 2 | WHERE | Descarta linhas, uma por uma | Ficam os 14 do Piauí |
| 3 | GROUP BY | Separa as linhas em grupos | 5 pilhas, uma por cidade |
| 4 | HAVING | Descarta grupos inteiros (próxima aula) | (não tem) |
| 5 | SELECT | Calcula as agregadas e as colunas de cada grupo; dá os apelidos | cidade, COUNT(*) |
| 6 | ORDER BY | Ordena as linhas do resultado | Mais clientes primeiro |
| 7 | LIMIT | Corta o resultado | (não tem) |
Duas consequências importantes:
- O
WHEREroda antes dos grupos existirem. Por isso ele não pode usarCOUNT,SUM… (dámisuse of aggregate function). Para filtrar pelo resultado de uma agregada, como "só as cidades com mais de 5 clientes", existe oHAVING, 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êmcidade= 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;
| Forma | Exemplo | Quando usar |
|---|---|---|
| Pelo nome da coluna | GROUP BY cidade | Sempre que puder: é a mais clara e funciona em todo banco |
| Pela expressão | GROUP BY substr(data, 1, 4) | Quando o grupo é uma conta; funciona em todo banco |
| Pelo apelido | GROUP BY ano | Mais legível com contas longas; aceito no SQLite e no PostgreSQL, mas não no SQL Server |
| Pela posição | GROUP BY 1 | Só em consultas rápidas, de teste |
| Só valores diferentes | SELECT DISTINCT cidade | Quando não precisa de agregada nenhuma |
Quando dá errado
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ó.
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.
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.
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.
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.
"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.)
- Coluna nua: PostgreSQL, MySQL (com o
ONLY_FULL_GROUP_BY, ligado por padrão) e SQL Server recusam colunas noSELECTque não estão noGROUP BYnem 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 doSELECT. Repita a expressão:GROUP BY SUBSTRING(data, 1, 4). - Ordem: a documentação do SQL Server avisa que o
GROUP BYnão garante a ordem do resultado. UseORDER BYem todo banco.
SELECT SUBSTRING(data, 1, 4) AS ano, COUNT(*) AS pedidos FROM pedidos GROUP BY SUBSTRING(data, 1, 4);
Referência rápida
| Escrita | O que faz |
|---|---|
GROUP BY coluna | Uma linha por valor diferente da coluna; as agregadas são calculadas por grupo |
GROUP BY expressão | Agrupa pelo resultado da conta (ex.: o ano da data) |
WHERE … GROUP BY … | Filtra as linhas antes de agrupar |
GROUP BY … ORDER BY COUNT(*) DESC | Ordena os grupos pela agregada |
NULL na coluna do grupo | Os NULL formam um grupo só |
| Ordem de escrita | SELECT → FROM → WHERE → GROUP BY → HAVING → ORDER BY → LIMIT |
| Ordem lógica | FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY → LIMIT |
| Mensagem do SQLite | Quer dizer |
|---|---|
near "WHERE": syntax error | WHERE escrito depois do GROUP BY |
near "GROUP": syntax error | GROUP BY escrito depois do ORDER BY |
aggregate functions are not allowed in the GROUP BY clause | Agregada dentro do GROUP BY |
1st GROUP BY term out of range - should be between 1 and 2 | Posiçã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):
2. Unidades por produto
Complete para somar as unidades vendidas de cada produto:
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 colunasepara as linhas em grupos (as pilhas) e devolve uma linha por grupo; as agregadas são calculadas em cada grupo.- O
WHEREfiltra 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)). OsNULLformam um grupo só. - No
SELECT, só colunas doGROUP BYou 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.