SQL GROUP BY e Funções de Agregação: Resuma Seus Dados
Published: 2026-04-08
GROUP BY colapsa múltiplas linhas que compartilham o mesmo valor em uma ou mais colunas em uma única linha de resumo. Combinado com funções de agregação como COUNT, SUM, AVG, MIN e MAX, ele é a base de quase todas as consultas de relatórios e análises. GROUP BY é suportado em todos os bancos de dados SQL, incluindo SQLite, PostgreSQL, MySQL, SQL Server e Oracle.
Dados de Exemplo
Usamos duas tabelas em todos os exemplos:
Tabela employees:
| id | name | department | hire_date | salary |
|---|---|---|---|---|
| 1 | Alice | Engineering | 2023-01-15 | 95000 |
| 2 | Bob | Engineering | 2023-06-01 | 85000 |
| 3 | Carol | Engineering | 2024-03-10 | 85000 |
| 4 | Dave | Sales | 2023-02-20 | 72000 |
| 5 | Eve | Sales | 2023-09-05 | 68000 |
| 6 | Frank | Sales | 2024-01-15 | 65000 |
| 7 | Grace | Marketing | 2023-04-01 | 82000 |
| 8 | Henry | Marketing | 2024-06-01 | 70000 |
Tabela orders:
| id | customer | product | category | quantity | unit_price | order_date | status |
|---|---|---|---|---|---|---|---|
| 1 | Acme Corp | Laptop Pro | Electronics | 3 | 1299.99 | 2026-01-05 | delivered |
| 2 | Acme Corp | Wireless Mouse | Electronics | 10 | 29.99 | 2026-01-12 | delivered |
| 3 | Globex | Office Chair | Furniture | 5 | 249.99 | 2026-01-18 | delivered |
| 4 | Globex | Standing Desk | Furniture | 2 | 599.99 | 2026-01-20 | shipped |
| 5 | Initech | Notebook Pack | Stationery | 20 | 12.99 | 2026-02-03 | delivered |
| 6 | Initech | Pen Set | Stationery | 15 | 8.99 | 2026-02-07 | delivered |
| 7 | Acme Corp | Monitor 27" | Electronics | 4 | 399.99 | 2026-02-14 | delivered |
| 8 | Umbrella Ltd | Laptop Pro | Electronics | 2 | 1299.99 | 2026-02-20 | cancelled |
| 9 | Umbrella Ltd | Desk Lamp | Furniture | 8 | 39.99 | 2026-03-01 | delivered |
| 10 | Globex | Wireless Mouse | Electronics | 12 | 29.99 | 2026-03-05 | shipped |
| 11 | Initech | Monitor 27" | Electronics | 1 | 399.99 | 2026-03-10 | delivered |
| 12 | Acme Corp | Standing Desk | Furniture | 1 | 599.99 | 2026-03-15 | processing |
Sintaxe de GROUP BY
SELECT column1, aggregate_function(column2)
FROM table_name
WHERE condition
GROUP BY column1
HAVING aggregate_condition
ORDER BY column1;
- GROUP BY — uma ou mais colunas cujas combinações de valores distintos definem cada grupo
- Funções de agregação — calculam um único valor a partir de todas as linhas do grupo
- HAVING — filtra grupos (como WHERE, mas para resultados agrupados)
- Toda coluna em SELECT que não estiver dentro de uma agregação deve aparecer em GROUP BY
Ordem de Execução do SQL
As cláusulas SQL são escritas em uma ordem, mas executadas em outra. Entender isso ajuda a explicar por que HAVING pode referenciar funções de agregação, mas WHERE não, e por que você não pode usar um alias de SELECT em uma cláusula HAVING.
FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY
| Etapa | Cláusula | O que acontece |
|---|---|---|
| 1 | FROM | Carrega a(s) tabela(s) e avalia quaisquer JOINs |
| 2 | WHERE | Filtra linhas individuais — funções de agregação não são permitidas aqui |
| 3 | GROUP BY | Colapsa as linhas restantes em grupos |
| 4 | HAVING | Filtra grupos — funções de agregação são permitidas aqui |
| 5 | SELECT | Calcula colunas de saída e aliases |
| 6 | ORDER BY | Ordena o conjunto de resultados final |
É por isso que WHERE status = 'delivered' é executado antes do agrupamento (rápido, filtro em nível de linha), enquanto HAVING COUNT(*) > 2 é executado após o agrupamento (filtra grupos inteiros).
COUNT()
COUNT(*) conta todas as linhas em um grupo. COUNT(column) conta valores não nulos nessa coluna.
Exemplo: Número de Funcionários por Departamento
Exemplo: Número de Pedidos por Cliente
Exemplo: Contar Entregues vs. Outros Status
Conte apenas pedidos que correspondem a uma condição usando COUNT com uma expressão CASE:
SUM()
SUM(column) soma todos os valores no grupo para uma coluna numérica.
Exemplo: Salário Total por Departamento
Exemplo: Receita Total por Categoria
Calcule a receita como quantity * unit_price:
Exemplo: Receita por Cliente e Categoria
Agrupe por múltiplas colunas para detalhar ainda mais a receita:
AVG()
AVG(column) retorna a média aritmética de todos os valores não nulos no grupo.
Exemplo: Salário Médio por Departamento
Exemplo: Valor Médio de Pedido por Cliente
MIN() e MAX()
MIN(column) e MAX(column) retornam os menores e maiores valores no grupo.
Exemplo: Faixa Salarial por Departamento
Exemplo: Primeira e Última Data de Pedido por Cliente
Combinando Múltiplas Agregações
Você pode combinar qualquer número de funções de agregação em uma única consulta.
Exemplo: Resumo Completo do Departamento
Exemplo: Resumo de Pedidos por Cliente
HAVING
HAVING filtra grupos após a agregação — é o equivalente a WHERE para resultados agrupados.
Exemplo: Departamentos com Mais de Dois Funcionários
Exemplo: Clientes com Receita Total Acima de 2000
Exemplo: Categorias com Valor Médio de Pedido Abaixo de 500
WHERE vs. HAVING
WHERE filtra linhas individuais antes do agrupamento. HAVING filtra grupos após a agregação.
Exemplo: Receita Apenas de Pedidos Entregues por Cliente
Use WHERE para excluir linhas antes do agrupamento e depois HAVING para filtrar resultados:
GROUP BY com JOINs
Você pode agrupar dados que abrangem múltiplas tabelas fazendo JOIN primeiro.
Exemplo: Receita por Departamento via Join com Employee
Exemplo: Contagem de Pedidos e Receita por Categoria e Mês
GROUP BY com Expressões
Você pode agrupar por expressões calculadas, não apenas por colunas simples.
Exemplo: Funcionários Contratados por Ano
Exemplo: Pedidos por Mês
COUNT(DISTINCT)
COUNT(DISTINCT column) conta o número de valores únicos no grupo.
Exemplo: Produtos Únicos Pedidos por Cliente
Filtrando Grupos com Múltiplas Condições
HAVING suporta qualquer expressão booleana, incluindo combinações AND/OR.
Exemplo: Clientes Ativos de Alto Valor
Encontre clientes com mais de um pedido e receita total acima de 1000:
Exemplo Prático: Relatório de Vendas
Combine tudo em um relatório completo de resumo de vendas.
Exemplo: Relatório de Desempenho por Categoria
Exemplo: Produto com Maior Receita
GROUP BY vs. Funções de Janela
GROUP BY colapsa linhas em uma linha por grupo. Funções de janela mantêm todas as linhas e adicionam uma coluna calculada — veja o tutorial de Funções de Janela em SQL para detalhes.
Exemplo: GROUP BY Retorna Uma Linha por Departamento
Exemplo: Função de Janela Mantém Todas as Linhas
Casos de Uso Comuns para GROUP BY
- Painéis de relatórios — Resumir totais, médias e contagens por categoria ou período de tempo
- Análise de headcount — Contar funcionários, usuários ou registros por grupo
- Detalhamento de receita — Receita total e média por produto, região ou cliente
- Análise de tendências — Agrupar por mês ou ano para identificar padrões ao longo do tempo
- Top-N por grupo — Combinar com subconsultas ou CTEs para encontrar o melhor desempenho em cada grupo
- Validação de dados — Contar duplicatas ou valores NULL entre grupos
- Análise de coorte — Agrupar usuários por data de cadastro ou segmento de comportamento
Dicas de Desempenho
- Indexe colunas de GROUP BY — Índices nas colunas agrupadas aceleram a etapa de ordenação e agrupamento
- Filtre cedo com WHERE — Reduzir linhas antes do agrupamento com WHERE é mais rápido do que filtrar depois com HAVING
- Evite SELECT * — Selecione apenas as colunas de que você realmente precisa; operações de agregação se beneficiam de dados mais estreitos
- Use índices abrangentes (covering indexes) — Um índice que inclui tanto a coluna de GROUP BY quanto a coluna agregada pode evitar um full table scan
- Considere views materializadas — Para agregações frequentemente calculadas em grandes tabelas, alguns bancos de dados suportam resumos pré-computados
Resumo
- GROUP BY colapsa linhas que compartilham os mesmos valores de coluna em uma linha de resumo por combinação única
- COUNT(*) conta todas as linhas; COUNT(column) conta valores não nulos; COUNT(DISTINCT column) conta valores únicos
- SUM totaliza valores numéricos; AVG calcula a média; MIN / MAX encontram os limites
- Toda coluna em SELECT deve aparecer em GROUP BY ou estar envolvida em uma função de agregação
- WHERE filtra linhas antes do agrupamento; HAVING filtra grupos após a agregação
- Você pode usar GROUP BY em expressões (por exemplo,
SUBSTR(date, 1, 7)para agrupamento em nível de mês) - Fazer JOIN entre tabelas antes de agrupar permite combinar dados de múltiplas fontes em um único relatório
- Use funções de janela em vez de GROUP BY quando precisar de métricas em nível de grupo sem perder as linhas individuais
Tente combinar GROUP BY com JOINs, expressões CASE e cláusulas HAVING para construir os relatórios de que sua análise de dados precisa!