SQL HAVING : filtrer les groupes
Après un GROUP BY, on veut parfois ne garder que certains groupes : les villes de plus de 10 clients, les catégories dont le prix moyen dépasse 20 €, les clients qui ont passé au moins 5 commandes. WHERE ne peut pas faire ce travail, car il agit avant le regroupement. Il faut HAVING.
Syntaxe
SELECT colonne, FONCTION(colonne)
FROM table
WHERE condition_sur_les_lignes
GROUP BY colonne
HAVING condition_sur_les_groupes;Comment ça marche
| WHERE | HAVING | |
|---|---|---|
| Agit sur | les lignes | les groupes |
| Moment | avant le GROUP BY | après le GROUP BY |
| Peut contenir COUNT, SUM… | non | oui |
On peut utiliser les deux dans la même requête : WHERE retire d'abord des lignes, GROUP BY forme les groupes avec celles qui restent, puis HAVING retire des groupes.
Quand une condition porte sur une colonne simple (et non sur une agrégation), mettez-la plutôt dans le WHERE : la base travaille alors sur moins de lignes.
Schéma
| ville | COUNT(*) |
|---|---|
| Lyon | 5 |
| Paris | 2 |
| Nantes | 3 |
| ville | COUNT(*) |
|---|---|
| Lyon | 5 |
| Nantes | 3 |
Schéma simplifié pour illustrer le principe. Les exemples ci-dessous utilisent la vraie base d'exercices de SQL Quest (une boutique en ligne fictive : voir le schéma complet).
Exemples avec leurs résultats
1. Garder les grands groupes
Les villes qui comptent plus de 10 clients.
SELECT ville, COUNT(*) AS nb_clients
FROM clients
GROUP BY ville
HAVING COUNT(*) > 10; Essayer dans le bac à sable| ville | nb_clients |
|---|---|
| Bordeaux | 14 |
| Lyon | 12 |
| Paris | 13 |
| Strasbourg | 11 |
4 lignes au total
2. Filtrer sur une moyenne
Les catégories dont le prix moyen dépasse 30 €.
SELECT categorie, ROUND(AVG(prix), 2) AS prix_moyen
FROM produits
GROUP BY categorie
HAVING AVG(prix) > 30; Essayer dans le bac à sable| categorie | prix_moyen |
|---|---|
| Informatique | 146,43 |
| Maison | 36,45 |
2 lignes au total
3. WHERE et HAVING ensemble
Parmi les commandes livrées seulement, les clients qui en ont au moins 6.
SELECT client_id, COUNT(*) AS livrees
FROM commandes
WHERE statut = 'livrée'
GROUP BY client_id
HAVING COUNT(*) >= 6
ORDER BY livrees DESC; Essayer dans le bac à sable| client_id | livrees |
|---|---|
| 39 | 8 |
| 10 | 8 |
| 74 | 6 |
3 lignes au total
Les erreurs fréquentes
Mettre l'agrégation dans WHERE
SQLite répond misuse of aggregate : au moment du WHERE, les groupes n'existent pas encore.
SELECT ville, COUNT(*) FROM clients WHERE COUNT(*) > 10 GROUP BY ville;SELECT ville, COUNT(*) FROM clients GROUP BY ville HAVING COUNT(*) > 10;Placer HAVING avant GROUP BY
HAVING vient toujours après GROUP BY dans l'écriture de la requête.
SELECT ville, COUNT(*) FROM clients HAVING COUNT(*) > 10 GROUP BY ville;SELECT ville, COUNT(*) FROM clients GROUP BY ville HAVING COUNT(*) > 10;Utiliser HAVING pour une condition simple
Ça marche, mais c'est moins efficace : filtrez les lignes dès le WHERE.
SELECT ville, COUNT(*) FROM clients GROUP BY ville HAVING ville <> 'Paris';SELECT ville, COUNT(*) FROM clients WHERE ville <> 'Paris' GROUP BY ville;Exercices
Essayez d'écrire la requête vous-même avant d'ouvrir la solution.
Défi 1. Affichez les départements dont le salaire moyen dépasse 4 000 €.
Voir une solution
SELECT departement, ROUND(AVG(salaire)) AS moyenne FROM employes GROUP BY departement HAVING AVG(salaire) > 4000;| departement | moyenne |
|---|---|
| Direction | 9 500 |
| IT | 4 760 |
| RH | 4 233 |
| Ventes | 4 554 |
4 lignes au total
Essayer dans le bac à sableDéfi 2. Affichez les commandes (commande_id) qui contiennent au moins 4 lignes.
Voir une solution
SELECT commande_id, COUNT(*) AS nb_lignes FROM lignes_commande GROUP BY commande_id HAVING COUNT(*) >= 4 LIMIT 10;| commande_id | nb_lignes |
|---|---|
| 4 | 4 |
| 5 | 4 |
| 7 | 4 |
| 10 | 4 |
| 14 | 4 |
| 22 | 4 |
| 24 | 4 |
| 27 | 4 |
10 lignes au total · 8 affichées
Essayer dans le bac à sableLes exercices interactifs sont corrigés automatiquement, avec un prof qui explique vos erreurs. Une leçon résumée reste affichée à côté de l'éditeur.
Questions fréquentes
Peut-on utiliser HAVING sans GROUP BY ?
Oui : la table entière forme alors un seul groupe. C'est rarement utile en pratique.
Peut-on utiliser un alias dans HAVING ?
SQLite et MySQL l'acceptent (HAVING nb_clients > 10). Pour du code portable, répétez l'expression : HAVING COUNT(*) > 10.
HAVING est-il plus lent que WHERE ?
Pour une même condition sur une colonne simple, oui, car HAVING s'applique après avoir regroupé toutes les lignes. Réservez HAVING aux conditions sur des agrégations.