GROUP BY et fonctions d’agrégation SQL : COUNT, SUM, AVG, MIN, MAX
Compter le nombre de commandes par client, calculer le chiffre d’affaires par région, trouver le salaire moyen par service… Dès qu’il s’agit de résumer des données plutôt que de les lister ligne par ligne, deux outils SQL entrent en jeu : les fonctions d’agrégation (COUNT, SUM, AVG, MIN, MAX) et la clause GROUP BY. Ensemble, ils transforment des milliers de lignes en une synthèse lisible.
Cet article explique comment les utiliser, seuls puis combinés, avec des exemples concrets et leur résultat. Il fait suite à notre présentation des requêtes SQL essentielles.
Le jeu de données d’exemple
Les exemples s’appuient sur une table employes :
| nom | service | salaire |
|---|---|---|
| Martin | Informatique | 3200 |
| Durand | Comptabilité | 2800 |
| Petit | Informatique | 3600 |
| Moreau | Comptabilité | 2900 |
| Leroy | Informatique | 4100 |
Les fonctions d’agrégation seules
Une fonction d’agrégation prend une colonne entière et en renvoie une seule valeur. Sans GROUP BY, elle s’applique à toute la table :
SELECT COUNT(*) AS nb_employes,
SUM(salaire) AS masse_salariale,
AVG(salaire) AS salaire_moyen,
MIN(salaire) AS salaire_min,
MAX(salaire) AS salaire_max
FROM employes;
Résultat :
| nb_employes | masse_salariale | salaire_moyen | salaire_min | salaire_max |
|---|---|---|---|---|
| 5 | 16600 | 3320 | 2800 | 4100 |
Chaque fonction a son rôle :
COUNT(*)compte les lignes.COUNT(colonne)compte les valeurs non NULL de cette colonne — une nuance importante.SUMadditionne,AVGfait la moyenne (sur les valeurs non NULL uniquement).MINetMAXrenvoient les valeurs extrêmes, y compris sur du texte ou des dates (ordre alphabétique, chronologique).
GROUP BY : agréger par groupe
C’est là que tout devient puissant. GROUP BY découpe les lignes en groupes partageant une même valeur, puis applique la fonction d’agrégation à chaque groupe séparément. Pour obtenir le salaire moyen par service :
SELECT service,
COUNT(*) AS effectif,
AVG(salaire) AS salaire_moyen
FROM employes
GROUP BY service;
Résultat :
| service | effectif | salaire_moyen |
|---|---|---|
| Informatique | 3 | 3633,33 |
| Comptabilité | 2 | 2850 |
La base a regroupé les 5 lignes en 2 groupes (un par service) et calculé l’effectif et la moyenne de chacun. La règle d’or à retenir : toute colonne du SELECT qui n’est pas dans une fonction d’agrégation doit figurer dans le GROUP BY. Sinon, la base renvoie une erreur (ou, pire, un résultat imprévisible sur certains moteurs permissifs).
HAVING : filtrer les groupes
Comment ne garder que les services dont le salaire moyen dépasse 3000 ? On pourrait penser à WHERE, mais il ne fonctionne pas ici : WHERE filtre les lignes avant le regroupement, alors qu’on veut filtrer après, sur le résultat de l’agrégation. C’est le rôle de HAVING :
SELECT service,
AVG(salaire) AS salaire_moyen
FROM employes
GROUP BY service
HAVING AVG(salaire) > 3000;
Résultat :
| service | salaire_moyen |
|---|---|
| Informatique | 3633,33 |
La Comptabilité (moyenne 2850) est écartée. Retenez la distinction, c’est la question d’entretien classique :
WHEREfiltre les lignes, avant leGROUP BY.HAVINGfiltre les groupes, après leGROUP BY, et peut porter sur une fonction d’agrégation.
L’ordre des clauses
Une requête complète respecte toujours cet ordre d’écriture — l’inverser provoque une erreur de syntaxe :
SELECT colonnes, agrégations
FROM table
WHERE condition sur les lignes
GROUP BY colonnes de regroupement
HAVING condition sur les groupes
ORDER BY tri du résultat;
Un exemple qui combine tout — l’effectif par service, en ne comptant que les salaires supérieurs à 2500, pour les services d’au moins 2 personnes, triés par effectif décroissant :
SELECT service, COUNT(*) AS effectif
FROM employes
WHERE salaire > 2500
GROUP BY service
HAVING COUNT(*) >= 2
ORDER BY effectif DESC;
GROUP BY et jointures : le duo gagnant
En pratique, on agrège très souvent le résultat d’une jointure. Par exemple, pour compter le nombre d’employés par libellé de service (quand le libellé est dans une autre table), on combine une jointure SQL et un GROUP BY :
SELECT s.libelle, COUNT(e.id_employe) AS effectif
FROM services s
LEFT JOIN employes e ON e.id_service = s.id_service
GROUP BY s.libelle;
Le LEFT JOIN garde tous les services (même vides), et COUNT(e.id_employe) renvoie 0 pour ceux sans employé — puisque COUNT d’une colonne ignore les NULL. C’est un motif extrêmement courant.
Points d’attention
- COUNT(*) vs COUNT(colonne) : le premier compte toutes les lignes, le second ignore les NULL de la colonne. Choisissez en connaissance de cause.
- AVG ignore les NULL : la moyenne se calcule sur les valeurs présentes, pas sur le nombre total de lignes. Si vous voulez traiter les NULL comme des zéros, utilisez
AVG(COALESCE(colonne, 0)). - Alias non utilisables dans HAVING/WHERE : sur la plupart des moteurs, vous ne pouvez pas réutiliser un alias du SELECT dans le HAVING ; répétez la fonction d’agrégation.
- Message d’erreur ? Un oubli de colonne dans le GROUP BY est une cause classique d’erreur ; voyez notre guide des erreurs Oracle courantes si vous êtes sur Oracle.
En résumé
Les fonctions d’agrégation résument une colonne en une valeur ; GROUP BY applique ce résumé groupe par groupe ; HAVING filtre ensuite les groupes obtenus. Gardez en tête la règle d’or (toute colonne non agrégée du SELECT va dans le GROUP BY) et la distinction WHERE / HAVING (avant / après regroupement). Avec ces principes, vous passez de longues listes de données à des synthèses claires — le cœur de l’analyse en SQL.