SQL HAVING

A gerente da Loja Aurora quer abrir um ponto de retirada nas cidades que já têm pelo menos 7 clientes. A Ana sabe contar clientes por cidade com GROUP BY, mas agora precisa descartar as cidades pequenas. A primeira tentativa dela é um WHERE COUNT(*) >= 7, e o SQLite responde com erro. O WHERE filtra linhas, uma por uma, antes de os grupos existirem. Para filtrar grupos, pelo resultado de uma agregada, o SQL tem outra cláusula: o HAVING ("tendo", em inglês), lido como "fique só com os grupos que tenham…".

Você vai aprender
  • Filtrar grupos com HAVING, usando COUNT, SUM, AVG…
  • A diferença entre WHERE (linhas, antes de agrupar) e HAVING (grupos, depois de agrupar).
  • Usar os dois juntos, e a ordem lógica completa de uma consulta.
  • Filtrar por uma agregada que nem aparece no resultado.
  • Por que HAVING SUM(…) = 30.94 não acha nada, e o ROUND que conserta.
  • Os erros reais do SQLite com HAVING e WHERE no lugar trocado.

A ideia: a peneira depois das pilhas

Lembra das fichas de cliente separadas em pilhas, uma por cidade (aula GROUP BY)? O HAVING é uma peneira de pilhas: depois de montadas e contadas, você olha cada pilha e só guarda as que passam no teste. Já o WHERE é uma peneira de fichas: tira fichas da mesa antes de montar as pilhas.

WHEREHAVING
FiltraLinhas (as fichas)Grupos (as pilhas)
QuandoAntes do GROUP BYDepois do GROUP BY
Pode usar agregadas?NãoSim: é para isso que ele existe
ExemploWHERE estado = 'PI'HAVING COUNT(*) >= 7

1. O primeiro HAVING: cidades com 7 clientes ou mais

SELECT cidade, COUNT(*) AS clientes
FROM clientes
GROUP BY cidade
HAVING COUNT(*) >= 7;
GROUP BY cidade
Monta as 8 pilhas, uma por cidade, e conta cada uma.
HAVING
"Tendo": a peneira dos grupos. Vem logo depois do GROUP BY.
COUNT(*) >= 7
O teste, feito em cada grupo: o grupo fica se tiver 7 linhas ou mais. Recife (2) e as cidades com 1 cliente são descartadas.

Sobraram Fortaleza, São Paulo e Teresina. Os grupos descartados somem por inteiro do resultado: não aparecem nem com zero.

Experimente: a consulta conta os produtos de cada categoria (8 linhas). Fique só com as categorias que têm 6 produtos ou mais.

SELECT categoria_id, COUNT(*) AS produtos
FROM produtos
GROUP BY categoria_id;
SELECT categoria_id, COUNT(*) AS produtos
FROM produtos
GROUP BY categoria_id
HAVING COUNT(*) >= 6;

2. HAVING com outras agregadas

Qualquer agregada serve no teste. Os pedidos com mais de R$ 500 em produtos:

SELECT pedido_id,
       ROUND(SUM(quantidade * preco_unitario), 2) AS total
FROM itens_pedido
GROUP BY pedido_id
HAVING SUM(quantidade * preco_unitario) > 500
ORDER BY total DESC;

Seis pedidos, do maior para o menor. Repare que o ORDER BY vem depois do HAVING: primeiro a peneira, depois a arrumação.

Uma agregada que nem aparece no resultado

O teste do HAVING não precisa estar no SELECT. Quantos produtos têm as categorias "caras", as de preço médio acima de R$ 50?

SELECT categoria_id, COUNT(*) AS produtos
FROM produtos
GROUP BY categoria_id
HAVING AVG(preco) > 50;

Eletrônicos (2) e Casa e Cozinha (3). A média foi calculada para cada grupo, usada na peneira e não foi mostrada. A documentação do SQLite diz isso com todas as letras: o HAVING pode usar valores, até agregadas, que não estão no resultado.


3. WHERE e HAVING juntos

A gerente muda de ideia: o ponto de retirada é para os clientes comuns (os VIPs já recebem em casa). Agora são duas peneiras, uma de cada tipo:

SELECT cidade, COUNT(*) AS clientes
FROM clientes
WHERE vip = 0
GROUP BY cidade
HAVING COUNT(*) >= 6;

Siga o caminho das linhas:

  1. FROM clientes: 30 linhas.
  2. WHERE vip = 0: tira as 5 fichas de VIP. Sobram 25 linhas.
  3. GROUP BY cidade: as 25 viram 8 pilhas (Teresina 8, São Paulo 6, Fortaleza 5, Recife 2 e quatro cidades com 1).
  4. HAVING COUNT(*) >= 6: ficam as pilhas com 6 ou mais. Sobram Teresina e São Paulo.
  5. SELECT: escreve a cidade e a contagem de cada pilha que sobrou.

Compare com o resultado da seção 1: Fortaleza tinha 7 clientes, mas 2 são VIP. Sem eles, a pilha de Fortaleza ficou com 5 e não passou na peneira. O WHERE mudou as pilhas, e com isso mudou o que o HAVING viu.

A ordem lógica completa

Agora você conhece todas as cláusulas desta parte do guia. A ordem em que o banco as "pensa":

PassoCláusulaTrabalha comPode usar agregadas?
1FROMA tabelaNão
2WHERECada linhaNão
3GROUP BYMonta os gruposNão
4HAVINGCada grupoSim
5SELECTCada grupo que sobrou; dá os apelidosSim
6ORDER BYAs linhas do resultadoSim
7LIMITCorta o resultadoNão

A regra de ouro sai da tabela: se o filtro usa uma agregada, ele vai no HAVING; se não usa, vai no WHERE. E a ordem de escrever é SELECT … FROM … WHERE … GROUP BY … HAVING … ORDER BY … LIMIT.

Um exemplo com todas as cláusulas: entre os pedidos de 2025 (os de id 33 a 64), os produtos que venderam 4 unidades ou mais, do mais vendido para o menos, só os 5 primeiros:

SELECT produto_id, SUM(quantidade) AS unidades
FROM itens_pedido
WHERE pedido_id BETWEEN 33 AND 64
GROUP BY produto_id
HAVING SUM(quantidade) >= 4
ORDER BY unidades DESC, produto_id
LIMIT 5;

4. Formas diferentes, e qual escolher

Filtro sem agregada: WHERE ou HAVING?

Um teste que não usa agregada, sobre a coluna do grupo, funciona nos dois lugares:

SELECT estado, COUNT(*) AS clientes
FROM clientes
WHERE estado = 'PI'
GROUP BY estado;
SELECT estado, COUNT(*) AS clientes
FROM clientes
GROUP BY estado
HAVING estado = 'PI';

Mesmo resultado, mas o WHERE é melhor: ele joga fora as linhas de outros estados antes de agrupar, e o banco trabalha menos. No HAVING, pela ordem lógica, o banco monta e conta as 4 pilhas para depois jogar 3 fora. Deixe o HAVING para os testes com agregadas.

O apelido no HAVING

SELECT cidade, COUNT(*) AS clientes
FROM clientes
GROUP BY cidade
HAVING clientes >= 7;

O SQLite aceita o apelido clientes no HAVING, mesmo ele sendo criado no SELECT, que vem depois na ordem lógica. É prático, mas não é padrão (veja "Nos outros bancos"). Repetir a agregada, HAVING COUNT(*) >= 7, funciona em todos.

SituaçãoUse
O filtro usa COUNT, SUM, AVG, MIN, MAXHAVING
O filtro olha colunas comuns, linha a linhaWHERE
Os dois tipos de filtroWHERE e HAVING juntos
Comparar uma soma de dinheiro com =HAVING ROUND(SUM(…), 2) = valor
Consulta que precisa rodar em qualquer bancoRepita a agregada no HAVING em vez do apelido

5. HAVING sem GROUP BY

Desde a versão 3.39, o SQLite aceita HAVING sem GROUP BY: a tabela inteira vira um grupo só, e o HAVING decide se essa única linha aparece ou não.

SELECT COUNT(*) AS clientes
FROM clientes
HAVING COUNT(*) > 3;
SELECT COUNT(*) AS clientes
FROM clientes
HAVING COUNT(*) > 100;

Na primeira, 30 > 3 e a linha aparece; na segunda, o resultado tem zero linhas. Repare na diferença: sem o HAVING, uma consulta com agregada sempre devolve uma linha; com ele, pode não devolver nenhuma. Na prática, quase sempre o HAVING vem com GROUP BY.


Quando dá errado

Agregada no WHERE

A primeira tentativa da Ana, a da introdução:

SELECT cidade, COUNT(*)
FROM clientes
WHERE COUNT(*) >= 7
GROUP BY cidade;

A mensagem: misuse of aggregate: COUNT(), "uso indevido de agregada: COUNT()". O WHERE roda no passo 2, antes de os grupos existirem: ainda não há o que contar. (Sem o GROUP BY, a mesma ideia sai com um texto um pouco diferente, misuse of aggregate function COUNT(); as duas querem dizer a mesma coisa.)

Como consertar: o teste com agregada vai no HAVING, depois do GROUP BY: GROUP BY cidade HAVING COUNT(*) >= 7.

O apelido no WHERE
SELECT cidade, COUNT(*) AS n
FROM clientes
WHERE n >= 7
GROUP BY cidade;

A mensagem: a mesma, misuse of aggregate: COUNT(). O SQLite entende que o n é o COUNT(*) do SELECT, e uma agregada continua proibida no WHERE, com ou sem apelido.

Como consertar: HAVING n >= 7 (ou HAVING COUNT(*) >= 7), depois do GROUP BY.

HAVING antes do GROUP BY
SELECT cidade, COUNT(*)
FROM clientes
HAVING COUNT(*) >= 7
GROUP BY cidade;

A mensagem: near "GROUP": syntax error. A ordem de escrita é fixa: o HAVING vem depois do GROUP BY, nunca antes.

HAVING numa consulta sem agregada
SELECT cidade
FROM clientes
HAVING COUNT(*) > 3;

A mensagem: HAVING clause on a non-aggregate query, "cláusula HAVING numa consulta que não é agregada". O SELECT não tem agregada nem GROUP BY, então não há grupos para o HAVING peneirar.

Como consertar: diga como agrupar: SELECT cidade FROM clientes GROUP BY cidade HAVING COUNT(*) > 3.

Comparar uma soma com =

A Ana procura o pedido cujo valor em produtos é exatamente 30.94 (ela sabe que é o pedido 1):

SELECT pedido_id
FROM itens_pedido
GROUP BY pedido_id
HAVING SUM(quantidade * preco_unitario) = 30.94;

O que aconteceu: zero linhas, sem erro. Como você viu na aula SUM(), a soma guardada fica uma fração minúscula abaixo de 30.94, e o = exige igualdade perfeita.

Como consertar: arredonde antes de comparar:

SELECT pedido_id
FROM itens_pedido
GROUP BY pedido_id
HAVING ROUND(SUM(quantidade * preco_unitario), 2) = 30.94;
Uma coluna comum no HAVING

A Ana quer contar, por cidade, os clientes que se chamam Ana:

SELECT cidade, COUNT(*) AS clientes
FROM clientes
GROUP BY cidade
HAVING nome LIKE 'Ana%';

O que aconteceu: São Paulo, com 7. Errado duas vezes: São Paulo tem só uma Ana, e a Ana Carvalho Lima, de Teresina, sumiu. O nome não está no GROUP BY nem numa agregada; no HAVING, o SQLite testou o nome de uma linha qualquer de cada grupo (a mesma ideia da coluna nua da aula MAX()). Por acaso, em São Paulo essa linha era a da Ana Beatriz.

Como consertar: filtro de linha vai no WHERE, antes de agrupar:

SELECT cidade, COUNT(*) AS clientes
FROM clientes
WHERE nome LIKE 'Ana%'
GROUP BY cidade;
Nos outros bancos
  • Apelido no HAVING: a documentação do PostgreSQL diz que o nome de uma coluna do resultado pode ser usado no ORDER BY e no GROUP BY, mas não no WHERE nem no HAVING: lá, repita a expressão. O MySQL aceita o apelido no HAVING e diz que isso é uma extensão dele ao SQL padrão. Repetir a agregada funciona em todos.
  • Coluna comum no HAVING: o PostgreSQL recusa uma coluna no HAVING que não esteja agrupada nem dentro de uma agregada, e o MySQL, com o ONLY_FULL_GROUP_BY ligado (o padrão), também. O resultado errado do último cenário, nesses bancos, vira um erro.
  • HAVING sem GROUP BY: o PostgreSQL também trata a tabela toda como um grupo só e devolve uma linha ou nenhuma, como o SQLite.
SELECT cidade, COUNT(*) AS clientes
FROM clientes
GROUP BY cidade
HAVING COUNT(*) >= 7;   -- HAVING clientes >= 7 não vale aqui

Referência rápida

EscritaO que faz
GROUP BY x HAVING COUNT(*) >= nSó os grupos com n linhas ou mais
HAVING SUM(…) > vSó os grupos cuja soma passa de v
HAVING AVG(…) > vPode filtrar por uma agregada que não está no SELECT
WHERE … GROUP BY … HAVING …Filtra linhas antes e grupos depois
HAVING ROUND(SUM(…), 2) = vCompara somas de dinheiro sem a sobrinha binária
HAVING apelido >= nAceito no SQLite e no MySQL, não no PostgreSQL
HAVING sem GROUP BYA tabela toda é um grupo: devolve uma linha ou nenhuma
Mensagem do SQLiteQuer dizer
misuse of aggregate: COUNT()Agregada (ou apelido de agregada) no WHERE de uma consulta com GROUP BY
misuse of aggregate function COUNT()Agregada no WHERE de uma consulta sem GROUP BY
near "GROUP": syntax errorHAVING escrito antes do GROUP BY
HAVING clause on a non-aggregate queryHAVING sem agregada no SELECT e sem GROUP BY

Exercícios

1. A peneira dos grupos

Complete para mostrar só as cidades com mais de 1 cliente:

SELECT cidade, COUNT(*) FROM clientes GROUP BY cidade COUNT(*) > 1;

2. Fretes altos

Complete para mostrar os funcionários cujos pedidos somam mais de R$ 220 de frete:

SELECT funcionario_id, SUM(frete) FROM pedidos GROUP BY funcionario_id HAVING (frete) > 220;

3. Onde vai o filtro "só os clientes do Ceará", numa consulta que conta clientes por cidade?

4. Qual é a ordem de escrita correta?

5. Por que WHERE COUNT(*) >= 7 dá erro?

6. Fortaleza tem 7 clientes, 2 deles VIP. Ela aparece em … WHERE vip = 0 GROUP BY cidade HAVING COUNT(*) >= 6?

Missão final: os clientes mais fiéis

Missão: a consulta conta os pedidos de cada cliente (26 linhas). A gerente quer mandar um brinde para os clientes que fizeram 4 pedidos ou mais. Deixe só esses.

SELECT cliente_id, COUNT(*) AS pedidos
FROM pedidos
GROUP BY cliente_id;
SELECT cliente_id, COUNT(*) AS pedidos
FROM pedidos
GROUP BY cliente_id
HAVING COUNT(*) >= 4;

Doze clientes ganham o brinde. Para mostrar o nome deles em vez do cliente_id, falta juntar a tabela clientes, o que você aprende em INNER JOIN. E se a gerente quiser agrupar por cidade e por VIP ao mesmo tempo, o próximo passo é Agrupando por Mais de uma Coluna.


Resumo

  • HAVING filtra grupos, depois do GROUP BY; o WHERE filtra linhas, antes. Os grupos que não passam somem do resultado.
  • Filtro com agregada (COUNT, SUM, AVG…) vai no HAVING; sem agregada, no WHERE. Agregada no WHERE dá misuse of aggregate.
  • A ordem lógica: FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY → LIMIT. O WHERE muda as pilhas que o HAVING vê.
  • O HAVING pode usar agregadas que não estão no SELECT.
  • Para comparar somas de dinheiro com =, use ROUND(SUM(…), 2).
  • Apelido no HAVING funciona no SQLite (e no MySQL), não no PostgreSQL; repetir a agregada funciona em todos. Coluna comum no HAVING testa uma linha qualquer do grupo: use WHERE.