Informática

SQL: GROUP BY, HAVING e funções de agregação com resultados

Agrupar linhas e resumi-las com COUNT, SUM, AVG, MIN e MAX é um dos temas de SQL mais cobrados, porque tem regras claras e pegadinhas previsíveis. Os exemplos abaixo usam dados fictícios e foram executados em um banco de teste para conferir cada número.

Dados de partida

Tabela servidor, a mesma dos outros guias de SQL deste site:

id nome unidade_id cargo horas_extras tempo_servico
1 Ana Lima 1 Analista 10 5
2 Bruno Souza 1 Tecnico 4 12
3 Carla Dias 2 Analista NULL 3
4 Diego Alves 2 Analista 8 9
5 Elisa Rocha 3 Tecnico 0 7
6 Fabio Nunes NULL Analista 6 2
7 Gabi Matos 2 Tecnico NULL 15

Tabela unidade: 1 Protocolo, 2 Compras, 3 Arquivo, 4 Ouvidoria.

Funções de agregação

Uma função de agregação recebe várias linhas e devolve um valor. As cinco clássicas:

Função O que faz
COUNT(*) conta linhas
COUNT(coluna) conta valores não nulos da coluna
SUM(coluna) soma, ignorando NULL
AVG(coluna) média, ignorando NULL
MIN / MAX menor e maior valor

Sobre a tabela inteira:

SELECT COUNT(*), COUNT(horas_extras), COUNT(DISTINCT cargo) FROM servidor;

Resultado: 7, 5 e 2. Há sete linhas, mas só cinco têm horas_extras preenchida, e existem dois cargos distintos.

SUM(horas_extras) é 28 (10+4+8+0+6). AVG(horas_extras) é 5,6: a soma 28 dividida por 5, não por 7, porque os dois NULL saem do cálculo. Se a regra de negócio diz que ausência vale zero, use AVG(COALESCE(horas_extras, 0)), que dá 28 / 7 = 4,0. A escolha muda a resposta, e a prova gosta disso.

GROUP BY

GROUP BY forma grupos de linhas com o mesmo valor na(s) coluna(s) indicada(s), e a agregação é calculada por grupo.

SELECT cargo, COUNT(*) FROM servidor GROUP BY cargo;

Resultado: Analista 4, Tecnico 3.

Agora por unidade, com várias agregações:

SELECT unidade_id, COUNT(*) AS n, SUM(horas_extras) AS tot,
       AVG(horas_extras) AS media, MAX(tempo_servico) AS maior
FROM servidor
GROUP BY unidade_id ORDER BY unidade_id;
unidade_id n tot media maior
NULL 1 6 6,0 2
1 2 14 7,0 12
2 3 8 8,0 15
3 1 0 0,0 7

Dois detalhes: o NULL forma um grupo próprio (Fabio), e na unidade 2 a média é 8,0 porque Carla e Gabi, com NULL, ficam fora do cálculo (só o valor de Diego conta). Para obter 2,67 (8 dividido por 3) seria preciso tratar o NULL como zero. A posição do grupo NULL na ordenação varia conforme o sistema.

Regra da lista do SELECT

Em uma consulta com GROUP BY, cada coluna do SELECT deve estar no GROUP BY ou dentro de uma função de agregação. SELECT nome, cargo, COUNT(*) ... GROUP BY cargo é inválido no padrão, pois nome não é único por grupo. Alguns sistemas aceitam por extensão, mas a resposta de prova segue o padrão.

Agrupar por duas colunas gera um grupo por combinação:

SELECT unidade_id, cargo, COUNT(*) FROM servidor
GROUP BY unidade_id, cargo ORDER BY unidade_id, cargo;

Saem seis grupos: (NULL, Analista) 1; (1, Analista) 1; (1, Tecnico) 1; (2, Analista) 2; (2, Tecnico) 1; (3, Tecnico) 1.

WHERE x HAVING

WHERE filtra linhas antes do agrupamento e não aceita função de agregação. HAVING filtra grupos depois do agrupamento e é onde se usa COUNT, SUM etc.

SELECT unidade_id, COUNT(*) AS n FROM servidor
GROUP BY unidade_id HAVING COUNT(*) >= 2 ORDER BY unidade_id;

Resultado: unidade 1 com 2 e unidade 2 com 3. Combinando os dois filtros:

SELECT unidade_id, COUNT(*) FROM servidor
WHERE cargo = 'Analista'
GROUP BY unidade_id HAVING COUNT(*) >= 2;

Primeiro o WHERE fica só com os quatro analistas; depois agrupa; depois o HAVING mantém grupos com 2 ou mais. Sobra apenas a unidade 2 (Carla e Diego), com contagem 2.

Ordem lógica de execução: FROM, WHERE, GROUP BY, HAVING, SELECT, ORDER BY.

Contar filhos com LEFT JOIN

Para contar quantos servidores cada unidade tem, incluindo as vazias:

SELECT u.nome, COUNT(s.id) AS qtd
FROM unidade u LEFT JOIN servidor s ON s.unidade_id = u.id
GROUP BY u.nome ORDER BY u.nome;

Resultado: Arquivo 1, Compras 3, Ouvidoria 0, Protocolo 2. Se trocar COUNT(s.id) por COUNT(*), a Ouvidoria passa a mostrar 1, porque a linha com NULL é uma linha. Esse é o erro mais comum de quem usa LEFT JOIN com agregação.

Erros comuns

  • Colocar COUNT(*) > 1 no WHERE em vez do HAVING.
  • Dizer que COUNT(coluna) conta nulos.
  • Esquecer que AVG ignora NULL e dividir pelo total de linhas.
  • Selecionar coluna não agrupada.
  • Achar que GROUP BY ordena o resultado; só ORDER BY garante ordem.

Fontes e atualização

Sem norma oficial brasileira sobre SQL, este guia segue o padrão da linguagem; os números foram obtidos executando as consultas em um banco de teste. Sistemas diferentes tratam colunas não agrupadas e a ordenação de NULL de modos distintos: confira o edital.

Próximos passos

Leia também

Para continuar no site

Viu algo errado, desatualizado ou confuso? Relatar erro nesta página