Grouper avec GROUP BY
Un agrégat global résume toute la table en une ligne. GROUP BY va plus loin : il divise les lignes en groupes selon une valeur commune, puis applique l'agrégat à chaque groupe séparément — non plus une ligne, mais une ligne par groupe.
De l'agrégat global au calcul par groupe
La leçon précédente donnait COUNT(*) = 4 — un seul chiffre pour toute la table. C'est utile pour un résumé global, mais si quelqu'un veut comparer les films oscarisés aux autres — combien dans chaque catégorie, quelle note moyenne par groupe — une seule ligne de résultat ne suffit plus.
GROUP BY règle ce problème : il regroupe les lignes qui partagent la même valeur dans une colonne, puis calcule l'agrégat dans chaque paquet séparément. Non plus une ligne en sortie, mais une ligne par groupe.
Table films (fictive) — la colonne oscar a des valeurs répétées
| titre | note | oscar |
|---|---|---|
| Inception | 8.8 | true |
| Le Parrain | 9.2 | true |
| Pulp Fiction | 8.9 | false |
| Amélie Poulain | 8.3 | false |
oscar vaut true pour deux films (Inception, Le Parrain) et false pour les deux autres (Pulp Fiction, Amélie Poulain). La valeur se répète — c'est la signature d'une colonne utile pour GROUP BY. PostgreSQL va former deux paquets, l'un pour chaque valeur, et calculer un agrégat dans chacun.
GROUP BY — un calcul par groupe
GROUP BY colonne regroupe toutes les lignes qui partagent la même valeur dans cette colonne, puis applique l'agrégat séparément à chaque groupe. La clause se place après le FROM (ou après un éventuel WHERE) :
SELECT oscar, COUNT(*) AS nb_films
FROM films
GROUP BY oscar
ORDER BY oscar| oscar | nb_films |
|---|---|
| false | 2 |
| true | 2 |
Deux lignes pour deux valeurs distinctes de oscar. COUNT(*) a été appliqué séparément à chaque groupe : les 2 films où oscar = false (Pulp Fiction, Amélie Poulain) et les 2 films où oscar = true (Le Parrain, Inception). Le total reste bien 4 (2 + 2). ORDER BY oscar trie false avant true.
La règle d'or
Règle à ne jamais oublier : toute colonne qui apparaît dans le SELECT sans être un agrégat doit figurer dans le GROUP BY. Si tu groupes par oscar, la colonne titre n'a pas de valeur unique par groupe — il y a deux titres dans le groupe oscar=true. PostgreSQL ne sait pas lequel afficher :
SELECT titre, COUNT(*) FROM films GROUP BY oscar → erreur : column "films.titre" must appear in the GROUP BY clause or be used in an aggregate function.
La correction est immédiate : remplacer titre par oscar dans le SELECT. On affiche la colonne sur laquelle on groupe, pas une autre :
SELECT oscar, COUNT(*) AS nb_films
FROM films
GROUP BY oscar
ORDER BY oscar| oscar | nb_films |
|---|---|
| false | 2 |
| true | 2 |
La règle s'applique à toutes les colonnes non-agrégées du SELECT. Si tu voulais afficher à la fois oscar et annee dans le SELECT, les deux devraient figurer dans le GROUP BY : GROUP BY oscar, annee — PostgreSQL créerait un groupe par combinaison de valeurs (oscar=true en 2010, oscar=true en 1972, etc.).
Plusieurs agrégats par groupe
GROUP BY n'est pas limité à COUNT(*). On peut associer AVG, MIN, MAX — ou les combiner dans la même requête. La dimension de groupement reste oscar ; seuls les calculs s'accumulent :
SELECT oscar,
COUNT(*) AS nb_films,
ROUND(AVG(note), 1) AS note_moyenne,
MIN(annee) AS premiere_sortie,
MAX(annee) AS derniere_sortie
FROM films
GROUP BY oscar
ORDER BY oscar| oscar | nb_films | note_moyenne | premiere_sortie | derniere_sortie |
|---|---|---|---|---|
| false | 2 | 8.6 | 1994 | 2001 |
| true | 2 | 9.0 | 1972 | 2010 |
Les films oscarisés ont une note moyenne plus haute (9.0 contre 8.6) et couvrent une période plus large (1972–2010 contre 1994–2001). La même structure GROUP BY … ORDER BY s'applique quelle que soit la colonne de groupement — les calculs restent identiques, seule la dimension change.
Trier les groupes
ORDER BY fonctionne après GROUP BY — et on peut trier sur le résultat de l'agrégat lui-même, pas seulement sur la colonne de groupement. L'ordre des clauses est toujours : FROM → WHERE → GROUP BY → ORDER BY.
SELECT oscar, ROUND(AVG(note), 1) AS note_moyenne
FROM films
GROUP BY oscar
ORDER BY note_moyenne DESC| oscar | note_moyenne |
|---|---|
| true | 9.0 |
| false | 8.6 |
ORDER BY note_moyenne DESC trie les groupes sur le résultat calculé — les films oscarisés en tête. Sur une table à six groupes ou plus (comme employes GROUP BY dept_id), ce tri révèle immédiatement les groupes dominants sans avoir à parcourir toute la liste. On peut trier sur l'alias (note_moyenne) ou directement sur l'expression AVG(note) — les deux fonctionnent.
Récapitulatif
Ce que tu retiens de cette leçon :
SELECT oscar, COUNT(*) AS nb_films, ROUND(AVG(note), 1) AS note_moyenneFROM filmsGROUP BY oscarORDER BY note_moyenne DESC💡 Bon à savoir : on peut grouper par plusieurs colonnes à la fois — GROUP BY colonne1, colonne2 crée un groupe par combinaison unique de valeurs, utile pour des analyses croisées. La leçon suivante introduit HAVING — le filtre qui s'applique aux groupes après le calcul, là où WHERE ne peut pas aller.
Passe à la pratique
Cette leçon t'a montré la théorie. Pour maîtriser SQL, rien ne vaut la pratique : accède aux exercices et au bac à sable PostgreSQL réel.