MÉMO - GROUP BY et fonctions d'agrégation SQL
Compter le nombre de commandes par client, calculer le chiffre d'affaires total par commercial, obtenir la moyenne des créances par catégorie de client : ce type de calcul, très fréquent dans les études de cas de gestion, ne se fait pas ligne par ligne mais par groupe. C'est exactement ce que permettent, combinées, les fonctions d'agrégation et la clause GROUP BY — un des blocs les plus fréquemment testés du programme UE8 du DCG.
Ici, l'enjeu est simple : comment demander à SQL de calculer un résultat par groupe de lignes, plutôt que sur l'ensemble ou ligne par ligne ?
Réponse rapide
| Question | Réponse |
| Calculer une valeur sur l'ensemble des lignes (sans regroupement) | utiliser une fonction d'agrégation seule dans le SELECT, sans GROUP BY |
| Calculer une valeur par sous-groupe (par client, par commercial, par mois...) | ajouter GROUP BY suivi de la ou des colonnes de regroupement |
| Filtrer les groupes obtenus (ex. ne garder que les clients ayant plus de 3 commandes) | utiliser HAVING, jamais WHERE, après le GROUP BY |
Les fonctions d'agrégation
| Fonction | Calcule |
| COUNT(*) | le nombre de lignes (COUNT(colonne) ignore les valeurs NULL de cette colonne) |
| SUM(colonne) | la somme des valeurs numériques de la colonne |
| AVG(colonne) | la moyenne des valeurs numériques de la colonne |
| MIN(colonne) | la plus petite valeur de la colonne |
| MAX(colonne) | la plus grande valeur de la colonne |
Exemple d'application
Table COMMANDE (numcommande, codeclient, montantht, datecommande).
On souhaite le nombre de commandes et le montant total facturé par client.
|
SELECT codeclient, COUNT(*) AS nb_commandes, SUM(montantht) AS total_ht |
|
FROM COMMANDE |
|
GROUP BY codeclient; |
Pour ne conserver que les clients ayant passé plus de 3 commandes, on ajoute un HAVING portant sur le résultat de l'agrégation :
|
SELECT codeclient, COUNT(*) AS nb_commandes, SUM(montantht) AS total_ht |
|
FROM COMMANDE |
|
GROUP BY codeclient |
|
HAVING COUNT(*) > 3; |
Si l'on veut en plus ne considérer que les commandes de l'année N, le filtre porte sur les lignes brutes et se place donc dans un WHERE, avant le GROUP BY :
|
SELECT codeclient, COUNT(*) AS nb_commandes, SUM(montantht) AS total_ht |
|
FROM COMMANDE |
|
WHERE datecommande BETWEEN '2026-01-01' AND '2026-12-31' |
|
GROUP BY codeclient |
|
HAVING COUNT(*) > 3; |
Erreurs fréquentes et bonnes pratiques
| Erreur | Bonne pratique |
| Filtrer un résultat de fonction d'agrégation avec WHERE | impossible : au moment où WHERE s'applique, l'agrégation n'a pas encore été calculée. Utiliser HAVING. |
| Sélectionner une colonne qui n'est ni agrégée ni présente dans le GROUP BY | un SGBD strict (norme SQL) rejette la requête ou renvoie une valeur indéterminée : chaque colonne du SELECT doit être soit une fonction d'agrégation, soit une colonne du GROUP BY |
| Confondre COUNT(*) et COUNT(colonne) | COUNT(*) compte toutes les lignes, COUNT(colonne) ignore les lignes où cette colonne est NULL — le résultat peut différer |
| Oublier qu'AVG ignore les valeurs NULL | une moyenne calculée avec AVG ne porte que sur les lignes où la colonne n'est pas NULL, pas sur l'ensemble des lignes du groupe |
En résumé
| Point clé | Réponse |
| Ordre des clauses avec agrégation | SELECT ... FROM ... WHERE ... GROUP BY ... HAVING ... ORDER BY ... |
| WHERE ou HAVING pour filtrer un total ou une moyenne calculés ? | toujours HAVING |
| Colonne autorisée dans le SELECT sans être agrégée ? | uniquement si elle figure aussi dans le GROUP BY |
Dans la bibliothèque Rodco
Essentiel
- Les requêtes SQL
Mémo
- Jointures SQL : implicite (WHERE) ou explicite (JOIN)
- Les sous-requêtes SQL
- INSERT, UPDATE, DELETE : le langage de manipulation des données ».



