L’instruction SQL HAVING filtre les groupes produits par GROUP BY en appliquant une condition sur une fonction d’agrégation comme COUNT, SUM, AVG, MIN ou MAX.
Table des matières
Syntaxe de base
La clause HAVING se place obligatoirement après GROUP BY et avant ORDER BY dans une requête. Elle occupe la quatrième position logique après FROM, WHERE et GROUP BY. Le moteur SQL exécute HAVING après le regroupement complet, ce qui lui permet d’accéder aux valeurs calculées par les fonctions d’agrégation.
SELECT categorie, COUNT(*) AS nb_produits
FROM produits
GROUP BY categorie
HAVING COUNT(*) > 20;
La condition COUNT(*) > 20 s’applique sur chaque groupe formé par GROUP BY, et non sur les lignes individuelles de la table. Les catégories contenant 20 produits ou moins sont exclues du résultat retourné.
Filtrer par nombre de lignes avec COUNT
COUNT(*) compte toutes les lignes de chaque groupe, y compris celles avec des valeurs NULL. HAVING COUNT(*) exclut ensuite les groupes dont le nombre de lignes ne satisfait pas la condition définie. Cette combinaison convient aux analyses de volume, comme identifier les catégories ou les clients les plus actifs.
La requête suivante conserve uniquement les villes qui regroupent plus de cinq clients dans la base.
SELECT ville, COUNT(*) AS nb_clients
FROM clients
GROUP BY ville
HAVING COUNT(*) > 5
ORDER BY nb_clients DESC;
Seules les villes avec plus de cinq clients apparaissent dans le résultat, triées par nombre décroissant. Vérifiez que la condition HAVING utilise la même expression que celle du SELECT pour éviter un calcul redondant par le moteur.
Filtrer par total avec SUM
SUM additionne les valeurs numériques d’une colonne pour chaque groupe avant que HAVING applique le filtre. Cette combinaison permet d’identifier les groupes dont le cumul dépasse ou reste sous un seuil défini. Elle convient aux analyses de chiffre d’affaires, de volumes de stock ou de montants financiers.
La requête suivante conserve uniquement les mois dont le chiffre d’affaires total dépasse 50 000 €.
SELECT mois, SUM(ventes) AS chiffre_affaires
FROM ventes_mensuelles
GROUP BY mois
HAVING SUM(ventes) > 50000
ORDER BY chiffre_affaires DESC;
Les mois sous le seuil de 50 000 € sont exclus du résultat, ce qui met en évidence les périodes les plus performantes. Utilisez un alias dans ORDER BY pour éviter de répéter l’expression SUM(ventes) deux fois dans la requête.
Filtrer par moyenne avec AVG
AVG calcule la moyenne des valeurs non NULL d’un groupe avant que HAVING évalue la condition. Cette combinaison convient à la segmentation de groupes selon un profil moyen, comme des régions par âge moyen ou des produits par prix moyen. Les valeurs NULL sont ignorées dans le calcul de la moyenne.
La requête suivante sélectionne les régions dont l’âge moyen des clients dépasse 40 ans.
SELECT region, AVG(age) AS age_moyen
FROM clients
GROUP BY region
HAVING AVG(age) > 40
ORDER BY age_moyen DESC;
Seules les régions dont la moyenne d’âge dépasse 40 ans apparaissent dans le résultat. Appliquez ROUND(AVG(age), 1) pour limiter l’affichage à une décimale si la précision complète n’est pas nécessaire.
Filtrer par valeurs extrêmes avec MIN et MAX
MIN et MAX retournent respectivement la valeur la plus basse et la plus haute d’un groupe. HAVING MIN() et HAVING MAX() filtrent les groupes selon ces valeurs extrêmes calculées. Ces fonctions s’appliquent aux nombres, aux dates et aux chaînes de caractères.
La requête suivante identifie les fournisseurs qui proposent au moins un produit à moins de 10 €.
SELECT fournisseur, MIN(prix_unitaire) AS prix_min
FROM produits
GROUP BY fournisseur
HAVING MIN(prix_unitaire) < 10
ORDER BY prix_min ASC;
Seuls les fournisseurs dont le produit le moins cher est sous 10 € apparaissent dans le résultat. Combinez MIN et MAX dans la même requête pour encadrer la plage de prix de chaque fournisseur.
Différence entre WHERE et HAVING
WHERE filtre les lignes individuelles avant le regroupement, tandis que HAVING filtre les groupes après le regroupement. Ces deux clauses peuvent coexister dans une même requête pour appliquer deux niveaux de filtrage distincts. WHERE ne peut jamais contenir une fonction d’agrégation comme COUNT ou SUM.
La requête suivante combine les deux clauses pour analyser les catégories rentables sur les commandes de 2026 uniquement.
SELECT categorie, SUM(ventes) AS total
FROM produits
WHERE date_vente >= '2026-01-01'
GROUP BY categorie
HAVING SUM(ventes) > 10000
ORDER BY total DESC;
WHERE réduit d’abord les lignes aux ventes de 2026, puis GROUP BY regroupe par catégorie, et HAVING exclut les catégories sous 10 000 €. Ces deux filtres opèrent à des étapes différentes du traitement de la requête.
Bonnes pratiques
Règle 1 — Toujours ajouter ORDER BY après HAVINGORDER BY SUM(ventes) DESC classe les groupes par importance décroissante et rend le résultat directement exploitable dans un rapport.
Règle 2 — Utiliser des alias pour les fonctions d’agrégationCOUNT(*) AS nb_commandes améliore la lisibilité du résultat et permet de référencer l’alias directement dans ORDER BY.
Règle 3 — Filtrer avec WHERE avant GROUP BY quand c’est possible
Réduire les lignes avec WHERE avant le regroupement réduit le volume traité par GROUP BY et améliore les performances sur les grandes tables.
Règle 4 — Indexer les colonnes utilisées dans GROUP BYCREATE INDEX idx_categorie ON produits(categorie) accélère le regroupement car le moteur trie les valeurs indexées sans parcourir toute la table.
Règle 5 — Tester sans HAVING d’abord
Exécutez d’abord la requête sans HAVING pour vérifier que GROUP BY produit les bons groupes, puis ajoutez HAVING pour filtrer les résultats.
Cas pratique métier
Un service commercial identifie les catégories de produits dont le chiffre d’affaires total dépasse 10 000 € sur le premier trimestre 2026, triées par performance décroissante.
SELECT categorie, SUM(prix * quantite) AS chiffre_affaires
FROM ventes
WHERE date_vente BETWEEN '2026-01-01' AND '2026-03-31'
GROUP BY categorie
HAVING SUM(prix * quantite) > 10000
ORDER BY chiffre_affaires DESC;
Erreurs courantes
❌ Erreur : HAVING placé avant GROUP BY
SELECT ville, COUNT(*) FROM clients HAVING COUNT(*) > 10 GROUP BY ville;
-- L'ordre des clauses est incorrect et génère une erreur de syntaxe fatale.
✅ Correct :
SELECT ville, COUNT(*) FROM clients GROUP BY ville HAVING COUNT(*) > 10;
❌ Erreur : WHERE utilisé pour filtrer un agrégat
SELECT ville, COUNT(*) FROM clients WHERE COUNT(*) > 10 GROUP BY ville;
-- WHERE s'exécute avant GROUP BY et ne connaît pas encore les agrégats.
✅ Correct :
SELECT ville, COUNT(*) FROM clients GROUP BY ville HAVING COUNT(*) > 10;
❌ Erreur : alias utilisé dans HAVING
SELECT categorie, SUM(ventes) AS total FROM ventes GROUP BY categorie HAVING total > 10000;
-- HAVING s'exécute avant SELECT sur certains SGBD : l'alias n'est pas encore disponible.
✅ Correct :
SELECT categorie, SUM(ventes) AS total FROM ventes GROUP BY categorie HAVING SUM(ventes) > 10000;
Sources: MySql , Postgresql