GROUP BY: agrupando resultados
1. Introdução ao GROUP BY
O GROUP BY é uma das cláusulas mais poderosas do SQL, permitindo transformar linhas individuais em grupos resumidos. Enquanto uma consulta comum retorna uma linha para cada registro da tabela, o GROUP BY agrega esses registros em grupos, aplicando funções de agregação para gerar informações consolidadas.
A sintaxe básica segue este padrão:
SELECT coluna_agrupamento, função_agregacao(coluna)
FROM nome_tabela
GROUP BY coluna_agrupamento;
Imagine uma tabela de vendas com milhares de pedidos. Sem GROUP BY, você veria cada pedido individualmente. Com GROUP BY, pode ver o total vendido por cliente, a média de valor por mês, ou a contagem de pedidos por vendedor.
2. Funcionamento Interno do GROUP BY
Internamente, o SQL processa o GROUP BY em duas etapas principais:
- Ordenação: O banco de dados ordena os registros pelos campos especificados no GROUP BY
- Agregação: Para cada grupo identificado, aplica as funções de agregação definidas no SELECT
As funções de agregação mais comuns são:
- COUNT() — conta registros
- SUM() — soma valores
- AVG() — calcula média
- MIN() — menor valor
- MAX() — maior valor
Regra fundamental: toda coluna presente no SELECT deve estar no GROUP BY ou ser passada como argumento de uma função de agregação. Caso contrário, o SQL retornará erro.
-- ERRADO: categoria não está no GROUP BY nem é agregada
SELECT cliente_id, categoria, SUM(valor)
FROM vendas
GROUP BY cliente_id;
-- CORRETO
SELECT cliente_id, SUM(valor)
FROM vendas
GROUP BY cliente_id;
3. Agrupamento com uma Única Coluna
Vamos criar um exemplo prático com uma tabela de vendas:
CREATE TABLE vendas (
id INT PRIMARY KEY,
cliente VARCHAR(100),
valor DECIMAL(10,2),
data_venda DATE
);
INSERT INTO vendas VALUES
(1, 'Ana', 1500.00, '2024-01-15'),
(2, 'Carlos', 800.00, '2024-01-20'),
(3, 'Ana', 2200.00, '2024-02-10'),
(4, 'Beatriz', 950.00, '2024-02-15'),
(5, 'Carlos', 3000.00, '2024-03-05');
Contando pedidos por cliente:
SELECT cliente, COUNT(*) AS total_pedidos
FROM vendas
GROUP BY cliente;
Resultado:
cliente | total_pedidos
Ana | 2
Beatriz | 1
Carlos | 2
Totalizando valores por cliente:
SELECT cliente, SUM(valor) AS total_gasto
FROM vendas
GROUP BY cliente;
Resultado:
cliente | total_gasto
Ana | 3700.00
Beatriz | 950.00
Carlos | 3800.00
4. Agrupamento com Múltiplas Colunas
Agrupar por múltiplas colunas cria combinações únicas entre elas. A sintaxe é:
GROUP BY coluna1, coluna2
Exemplo prático: total de vendas por cliente e mês:
SELECT
cliente,
EXTRACT(MONTH FROM data_venda) AS mes,
SUM(valor) AS total_mes
FROM vendas
GROUP BY cliente, EXTRACT(MONTH FROM data_venda)
ORDER BY cliente, mes;
Resultado:
cliente | mes | total_mes
Ana | 1 | 1500.00
Ana | 2 | 2200.00
Beatriz | 2 | 950.00
Carlos | 1 | 800.00
Carlos | 3 | 3000.00
A hierarquia de agrupamento considera todas as combinações distintas dos valores das colunas listadas.
5. Ordenação de Grupos com ORDER BY
O ORDER BY pode ser combinado com GROUP BY para organizar os resultados. É possível ordenar tanto por colunas de agrupamento quanto por colunas agregadas:
SELECT cliente, SUM(valor) AS total_gasto
FROM vendas
GROUP BY cliente
ORDER BY total_gasto DESC;
Resultado:
cliente | total_gasto
Carlos | 3800.00
Ana | 3700.00
Beatriz | 950.00
Ordenando por coluna de agrupamento:
SELECT cliente, COUNT(*) AS pedidos
FROM vendas
GROUP BY cliente
ORDER BY cliente ASC;
6. Diferença entre GROUP BY e HAVING
É essencial entender a separação de responsabilidades:
- WHERE: filtra linhas antes do agrupamento
- GROUP BY: cria os grupos
- HAVING: filtra grupos após a agregação
Exemplo: queremos clientes que gastaram mais de R$ 1000,00 no total:
SELECT cliente, SUM(valor) AS total_gasto
FROM vendas
WHERE valor > 0 -- filtra linhas individuais
GROUP BY cliente
HAVING SUM(valor) > 1000.00 -- filtra grupos
ORDER BY total_gasto DESC;
Resultado:
cliente | total_gasto
Carlos | 3800.00
Ana | 3700.00
Note que Beatriz (R$ 950,00) foi excluída pelo HAVING.
7. Cuidados e Boas Práticas
Erro comum: colocar colunas não agregadas fora do GROUP BY.
-- ERRADO
SELECT cliente, data_venda, SUM(valor)
FROM vendas
GROUP BY cliente;
-- CORRETO (se quiser agrupar por cliente e data)
SELECT cliente, data_venda, SUM(valor)
FROM vendas
GROUP BY cliente, data_venda;
Desempenho: índices nas colunas usadas no GROUP BY podem acelerar significativamente a consulta, especialmente em tabelas grandes.
Clareza com alias: sempre nomeie suas colunas agregadas com alias descritivos:
SELECT
cliente,
COUNT(*) AS total_pedidos,
SUM(valor) AS receita_total,
AVG(valor) AS ticket_medio
FROM vendas
GROUP BY cliente;
8. Exemplo Completo Passo a Passo
Vamos criar um cenário realista de pedidos:
-- Criação da tabela
CREATE TABLE pedidos (
id INT PRIMARY KEY,
cliente VARCHAR(100),
produto VARCHAR(100),
quantidade INT,
preco_unitario DECIMAL(10,2),
data_pedido DATE,
status VARCHAR(20)
);
-- Inserção de dados
INSERT INTO pedidos VALUES
(1, 'Ana', 'Notebook', 1, 3500.00, '2024-01-10', 'Entregue'),
(2, 'Carlos', 'Mouse', 3, 150.00, '2024-01-12', 'Entregue'),
(3, 'Ana', 'Teclado', 2, 200.00, '2024-01-15', 'Entregue'),
(4, 'Beatriz', 'Monitor', 1, 1200.00, '2024-02-01', 'Pendente'),
(5, 'Carlos', 'Notebook', 1, 3500.00, '2024-02-05', 'Entregue'),
(6, 'Ana', 'Mouse', 5, 150.00, '2024-02-10', 'Pendente'),
(7, 'Beatriz', 'Teclado', 1, 200.00, '2024-02-15', 'Entregue'),
(8, 'Carlos', 'Monitor', 2, 1200.00, '2024-03-01', 'Pendente');
Consulta completa com COUNT, SUM, AVG, HAVING e ORDER BY:
SELECT
cliente,
COUNT(*) AS total_pedidos,
SUM(quantidade * preco_unitario) AS valor_total,
AVG(quantidade * preco_unitario) AS ticket_medio
FROM pedidos
WHERE status = 'Entregue'
GROUP BY cliente
HAVING COUNT(*) >= 2
ORDER BY valor_total DESC;
Resultado:
cliente | total_pedidos | valor_total | ticket_medio
Ana | 3 | 4200.00 | 1400.00
Carlos | 2 | 3800.00 | 1900.00
O que aconteceu:
1. WHERE status = 'Entregue' filtrou apenas pedidos entregues (excluiu pedidos de Beatriz e um de Carlos)
2. GROUP BY cliente agrupou por cliente
3. COUNT, SUM e AVG calcularam as métricas por grupo
4. HAVING COUNT(*) >= 2 manteve apenas clientes com 2 ou mais pedidos (excluiu Beatriz, que tinha 1)
5. ORDER BY valor_total DESC ordenou do maior para o menor valor
Este exemplo demonstra como GROUP BY transforma dados brutos em informações gerenciais valiosas, essenciais para análises de negócio, relatórios financeiros e dashboards.
Referências
- Documentação oficial MySQL: GROUP BY — Detalhes completos sobre a sintaxe e comportamento do GROUP BY no MySQL
- PostgreSQL Documentation: GROUP BY — Explicação oficial do PostgreSQL sobre agrupamento e funções de agregação
- SQL Server: GROUP BY (Transact-SQL) — Documentação Microsoft com exemplos práticos de GROUP BY no SQL Server
- W3Schools: SQL GROUP BY Statement — Tutorial interativo com exemplos básicos e intermediários de GROUP BY
- SQL Tutorial: GROUP BY and HAVING — Guia completo com exercícios práticos sobre GROUP BY, HAVING e funções de agregação
- Oracle Live SQL: Group By and Aggregation — Tutorial oficial Oracle sobre agrupamento e agregação de dados