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…".
- Filtrar grupos com
HAVING, usandoCOUNT,SUM,AVG… - A diferença entre
WHERE(linhas, antes de agrupar) eHAVING(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.94não acha nada, e oROUNDque conserta. - Os erros reais do SQLite com
HAVINGeWHEREno 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.
WHERE | HAVING | |
|---|---|---|
| Filtra | Linhas (as fichas) | Grupos (as pilhas) |
| Quando | Antes do GROUP BY | Depois do GROUP BY |
| Pode usar agregadas? | Não | Sim: é para isso que ele existe |
| Exemplo | WHERE 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:
- FROM clientes: 30 linhas.
- WHERE vip = 0: tira as 5 fichas de VIP. Sobram 25 linhas.
- GROUP BY cidade: as 25 viram 8 pilhas (Teresina 8, São Paulo 6, Fortaleza 5, Recife 2 e quatro cidades com 1).
- HAVING COUNT(*) >= 6: ficam as pilhas com 6 ou mais. Sobram Teresina e São Paulo.
- 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":
| Passo | Cláusula | Trabalha com | Pode usar agregadas? |
|---|---|---|---|
| 1 | FROM | A tabela | Não |
| 2 | WHERE | Cada linha | Não |
| 3 | GROUP BY | Monta os grupos | Não |
| 4 | HAVING | Cada grupo | Sim |
| 5 | SELECT | Cada grupo que sobrou; dá os apelidos | Sim |
| 6 | ORDER BY | As linhas do resultado | Sim |
| 7 | LIMIT | Corta o resultado | Nã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ção | Use |
|---|---|
O filtro usa COUNT, SUM, AVG, MIN, MAX | HAVING |
| O filtro olha colunas comuns, linha a linha | WHERE |
| Os dois tipos de filtro | WHERE e HAVING juntos |
Comparar uma soma de dinheiro com = | HAVING ROUND(SUM(…), 2) = valor |
| Consulta que precisa rodar em qualquer banco | Repita 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
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.
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.
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.
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.
=
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;
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;
- Apelido no HAVING: a documentação do PostgreSQL diz que o nome de uma coluna do resultado pode ser usado no
ORDER BYe noGROUP BY, mas não noWHEREnem noHAVING: lá, repita a expressão. O MySQL aceita o apelido noHAVINGe 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
HAVINGque não esteja agrupada nem dentro de uma agregada, e o MySQL, com oONLY_FULL_GROUP_BYligado (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
| Escrita | O que faz |
|---|---|
GROUP BY x HAVING COUNT(*) >= n | Só os grupos com n linhas ou mais |
HAVING SUM(…) > v | Só os grupos cuja soma passa de v |
HAVING AVG(…) > v | Pode filtrar por uma agregada que não está no SELECT |
WHERE … GROUP BY … HAVING … | Filtra linhas antes e grupos depois |
HAVING ROUND(SUM(…), 2) = v | Compara somas de dinheiro sem a sobrinha binária |
HAVING apelido >= n | Aceito no SQLite e no MySQL, não no PostgreSQL |
HAVING sem GROUP BY | A tabela toda é um grupo: devolve uma linha ou nenhuma |
| Mensagem do SQLite | Quer 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 error | HAVING escrito antes do GROUP BY |
HAVING clause on a non-aggregate query | HAVING 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:
2. Fretes altos
Complete para mostrar os funcionários cujos pedidos somam mais de R$ 220 de frete:
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
HAVINGfiltra grupos, depois doGROUP BY; oWHEREfiltra linhas, antes. Os grupos que não passam somem do resultado.- Filtro com agregada (
COUNT,SUM,AVG…) vai noHAVING; sem agregada, noWHERE. Agregada noWHEREdámisuse of aggregate. - A ordem lógica:
FROM→WHERE→GROUP BY→HAVING→SELECT→ORDER BY→LIMIT. OWHEREmuda as pilhas que oHAVINGvê. - O
HAVINGpode usar agregadas que não estão noSELECT. - Para comparar somas de dinheiro com
=, useROUND(SUM(…), 2). - Apelido no
HAVINGfunciona no SQLite (e no MySQL), não no PostgreSQL; repetir a agregada funciona em todos. Coluna comum noHAVINGtesta uma linha qualquer do grupo: useWHERE.