GROUP BY e HAVING
O que e GROUP BY?
Ate agora, todas as consultas retornavam linhas individuais. Mas e quando voce quer respostas como "quantos alunos tem em cada cidade?" ou "qual a media de nota por turma?"? E ai que entra o GROUP BY: ele junta linhas que tem o mesmo valor em uma coluna e permite usar funcoes de agregacao em cada grupo.
Funcoes de agregacao
Antes de agrupar, voce precisa conhecer as funcoes que trabalham com grupos:
| Funcao | O que faz |
|---|---|
COUNT(*) | Conta quantas linhas tem no grupo |
SUM() | Soma os valores da coluna |
AVG() | Calcula a media dos valores |
MIN() | Retorna o menor valor |
MAX() | Retorna o maior valor |
-- Sem GROUP BY, a funcao age em TODAS as linhas
SELECT COUNT(*) AS total FROM alunos;
-- → 150
SELECT AVG(nota) AS media_geral FROM alunos;
-- → 7.45
GROUP BY na pratica
Imagine a tabela alunos:
| id | nome | cidade | nota |
|---|---|---|---|
| 1 | Ana | Sao Paulo | 8.5 |
| 2 | Bruno | Curitiba | 7.0 |
| 3 | Carla | Sao Paulo | 9.0 |
| 4 | Diego | Curitiba | 6.5 |
| 5 | Elena | Belo Horizonte | 8.0 |
SELECT cidade, COUNT(*) AS total_alunos
FROM alunos
GROUP BY cidade;
Resultado:
| cidade | total_alunos |
|---|---|
| Sao Paulo | 2 |
| Curitiba | 2 |
| Belo Horizonte | 1 |
-- Media de nota por cidade
SELECT cidade, AVG(nota) AS media_nota
FROM alunos
GROUP BY cidade;
| cidade | media_nota |
|---|---|
| Sao Paulo | 8.75 |
| Curitiba | 6.75 |
| Belo Horizonte | 8.00 |
WHERE vs HAVING
Essa e uma das confusoes mais comuns em SQL. A regra e simples:
- WHERE filtra linhas ANTES do agrupamento
- HAVING filtra grupos DEPOIS do agrupamento
-- WHERE: filtra linhas antes de agrupar
-- So considera alunos com nota >= 7
SELECT cidade, COUNT(*) AS total_aprovados
FROM alunos
WHERE nota >= 7
GROUP BY cidade;
| cidade | total_aprovados |
|---|---|
| Sao Paulo | 2 |
| Curitiba | 1 |
| Belo Horizonte | 1 |
-- HAVING: filtra grupos depois de agrupar
-- So mostra cidades com mais de 1 aluno
SELECT cidade, COUNT(*) AS total_alunos
FROM alunos
GROUP BY cidade
HAVING COUNT(*) > 1;
| cidade | total_alunos |
|---|---|
| Sao Paulo | 2 |
| Curitiba | 2 |
Combinando WHERE e HAVING
Voce pode usar os dois juntos. O WHERE filtra primeiro, depois o GROUP BY agrupa, e o HAVING filtra os grupos:
-- Cidades com mais de 1 aluno aprovado (nota >= 7)
SELECT cidade, COUNT(*) AS aprovados
FROM alunos
WHERE nota >= 7
GROUP BY cidade
HAVING COUNT(*) > 1;
A ordem de execucao no banco e: FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY.
GROUP BY com ORDER BY
Voce pode ordenar o resultado agrupado:
SELECT cidade, COUNT(*) AS total
FROM alunos
GROUP BY cidade
ORDER BY total DESC;
GROUP BY nos ORMs
Prisma
const alunosPorCidade = await prisma.aluno.groupBy({
by: ['cidade'],
_count: { id: true },
_avg: { nota: true },
having: {
nota: { _avg: { gt: 7 } }
}
})
Drizzle
import { sql, count, avg } from 'drizzle-orm'
const resultado = await db
.select({
cidade: alunos.cidade,
total: count(),
media: avg(alunos.nota)
})
.from(alunos)
.groupBy(alunos.cidade)
.having(sql`count(*) > 1`)
Lucid (AdonisJS)
const resultado = await Aluno
.query()
.select('cidade')
.count('* as total')
.avg('nota as media_nota')
.groupBy('cidade')
.having('count(*)', '>', 1)
Exercicios
- Escreva uma consulta que retorne a quantidade de alunos por cidade, ordenada da maior para a menor quantidade.
- Escreva uma consulta que retorne a maior nota por cidade.
- Escreva uma consulta que mostre apenas cidades onde a media de notas e maior que 7.
- Usando WHERE e HAVING juntos, retorne cidades com mais de 2 alunos que tem nota acima de 6.
Referencias
- GROUP BY - PostgreSQL Docs -- documentacao oficial
- SQL GROUP BY Statement -- W3Schools com exemplos interativos
- Curso SQL #10 - GROUP BY e HAVING -- Curso em Video
Testa o que você leu
5 perguntas sobre esta aula. Errar aqui não custa nada - a explicação vem junto da correção.