Une agrégation résume plusieurs lignes en une seule valeur : count(*)
compte, sum additionne, avg fait la moyenne ; avec GROUP BY, on obtient
une ligne de résumé par groupe, par exemple un nombre de déploiements par
environnement. C'est la base de tout tableau de bord d'exploitation : taux
de réussite des déploiements, durée moyenne, alertes par serveur. Cette leçon
pratique les agrégats, GROUP BY, HAVING et les fonctions de dates et de
chaînes sur la base fil rouge, sous PostgreSQL 18.6, avec deux pièges qui
rendent un chiffre faux sans erreur.
Ce que vous allez apprendre
Section intitulée « Ce que vous allez apprendre »- Résumer une table avec
COUNT,SUM,AVG,MINetMAX. - Regrouper avec
GROUP BYet filtrer les groupes avecHAVING. - Calculer un taux sans tomber dans la division entière.
- Regrouper et calculer sur des dates et des chaînes.
Prérequis
Section intitulée « Prérequis »Les leçons Syntaxe de base et NULL et valeurs absentes, qui explique comment les agrégats traitent les valeurs absentes.
Résumer une table entière
Section intitulée « Résumer une table entière »Sans GROUP BY, une fonction d'agrégation résume toute la table, ou
toutes les lignes que garde WHERE, en une seule ligne :
SELECT count(*) AS deploiements, min(deployed_at) AS premier, max(deployed_at) AS dernier, round(avg(duration_seconds), 1) AS duree_moy, sum(duration_seconds) AS duree_totaleFROM deployments; deploiements | premier | dernier | duree_moy | duree_totale--------------+------------------------+------------------------+-----------+-------------- 25 | 2026-01-15 09:00:00+00 | 2026-04-13 02:30:00+00 | 77.4 | 1934round(valeur, 1) arrondit à une décimale. La moyenne cache souvent une
distribution déséquilibrée : la médiane, que PostgreSQL calcule avec
percentile_cont(0.5) WITHIN GROUP (ORDER BY duration_seconds), vaut 48
secondes, quand la moyenne en affiche 77,4. Quelques déploiements de base de
données, à 300 secondes, tirent la moyenne vers le haut.
Une ligne par groupe avec GROUP BY
Section intitulée « Une ligne par groupe avec GROUP BY »GROUP BY découpe les lignes en groupes de même valeur, et calcule
les agrégats par groupe :
SELECT environment, count(*) AS n, round(avg(duration_seconds)) AS duree_moyFROM deploymentsGROUP BY environmentORDER BY n DESC; environment | n | duree_moy-------------+----+----------- production | 17 | 71 staging | 7 | 102 development | 1 | 10On peut regrouper sur plusieurs colonnes : GROUP BY environment, status
donne une ligne par combinaison, et montre les 2 rollbacks de
production et les 2 échecs de staging.
La colonne qui n'est ni groupée ni agrégée
Section intitulée « La colonne qui n'est ni groupée ni agrégée »Dans une requête groupée, chaque colonne du SELECT doit être dans le
GROUP BY ou dans une agrégation : un groupe de 17 déploiements n'a pas
une seule version. PostgreSQL refuse :
ERROR: column "deployments.version" must appear in the GROUP BY clause or be used in an aggregate functionMySQL 8.4 refuse aussi, grâce au mode ONLY_FULL_GROUP_BY activé par
défaut. SQLite, lui, accepte la même requête et affiche une version arbitraire
par groupe, 2.1.0 pour la production, sans le moindre avertissement : un
chiffre faux qui a l'air juste. Une requête mise au point sous SQLite peut donc
échouer en production sous PostgreSQL, et c'est tant mieux.
Filtrer les groupes avec HAVING
Section intitulée « Filtrer les groupes avec HAVING »WHERE filtre les lignes avant le regroupement ; HAVING filtre
les groupes après. Une condition sur un agrégat va donc dans HAVING :
WHERE count(*) > 5 est refusé avec
aggregate functions are not allowed in WHERE. Pour trouver les membres de l'équipe qui ont au moins deux
déploiements en échec ou annulés :
SELECT u.username, count(*) AS echecsFROM deployments dJOIN users u ON u.id = d.deployed_byWHERE d.status IN ('failed', 'rollback') -- avant : quelles lignesGROUP BY u.usernameHAVING count(*) >= 2 -- après : quels groupesORDER BY echecs DESC;Mesuré, un seul nom ressort : agarcia, avec 3. Garder dans WHERE
tout ce qui peut y aller est aussi plus efficace : moins de lignes à regrouper.
Calculer un taux sans division entière
Section intitulée « Calculer un taux sans division entière »Un taux de réussite se calcule en divisant deux comptes. FILTER, une
clause du standard SQL que PostgreSQL implémente, restreint un agrégat à
certaines lignes, sans sous-requête :
SELECT environment, count(*) AS total, count(*) FILTER (WHERE status = 'success') AS reussis, round(100.0 * count(*) FILTER (WHERE status = 'success') / count(*), 1) AS tauxFROM deploymentsGROUP BY environmentORDER BY environment; environment | total | reussis | taux-------------+-------+---------+------- development | 1 | 1 | 100.0 production | 17 | 15 | 88.2 staging | 7 | 5 | 71.4Le 100.0 n'est pas décoratif. count(*) rend un entier, et la
division de deux entiers est entière : 7 / 2 vaut 3. Mesuré, la même
requête écrite count(*) FILTER (...) / count(*) * 100 affiche un taux de
0 pour la production et le staging, parce que 15 / 17 vaut 0 avant même
la multiplication. Commencer par 100.0 *, ou convertir avec ::numeric,
fait passer tout le calcul en décimal.
Calculer sur des dates
Section intitulée « Calculer sur des dates »Les colonnes de dates sont des timestamptz, avec leurs fonctions propres.
date_trunc ramène une date au début de sa période, ce qui permet de
regrouper par mois, jour ou heure :
SELECT date_trunc('month', deployed_at) AS mois, count(*)FROM deploymentsGROUP BY 1ORDER BY 1;Mesuré : 2 déploiements en janvier, 4 en février, 7 en mars, 12 en avril.
GROUP BY 1 désigne la première colonne du SELECT, un raccourci
commode. Les autres fonctions utiles :
| Fonction | Exemple | Résultat mesuré |
|---|---|---|
extract | extract(dow FROM deployed_at) | jour de la semaine, 0 pour dimanche : 9 déploiements |
to_char | to_char(created_at, 'DD/MM/YYYY') | 15/06/2023 |
| soustraction | acknowledged_at - created_at | un intervalle, 01:00:00 au plus long |
avg d'intervalles | avg(acknowledged_at - created_at) | délai moyen d'acquittement, 00:17:30 |
interval | now() - interval '24 hours' | il y a 24 heures |
Soustraire deux horodatages donne un interval, qui s'agrège comme un
nombre : le délai moyen d'acquittement des alertes est de 17 minutes 30.
Les données de la base fil rouge datent d'avril 2026 : une fenêtre
« dernières 24 heures » calculée avec now() y est vide, d'où des exemples à
date fixe.
Calculer sur des chaînes
Section intitulée « Calculer sur des chaînes »Les fonctions de chaînes servent surtout à extraire une information
rangée dans un texte. split_part(hostname, '-', 1) découpe le nom
d'hôte sur le tiret et garde le premier morceau, le rôle du serveur, sur
lequel on peut regrouper :
SELECT split_part(hostname, '-', 1) AS role, count(*)FROM serversGROUP BY 1ORDER BY 2 DESC, 1;Les serveurs db arrivent en tête, avec 3. string_agg fait l'inverse
d'un découpage : elle assemble les valeurs d'un groupe en une chaîne,
string_agg(hostname, ', ' ORDER BY hostname) liste les serveurs de chaque
datacenter sur une ligne. upper, length et replace complètent la boîte à
outils, et l'opérateur ~ teste une expression régulière :
hostname ~ '^(web|app)-' garde app-staging-01, web-prod-01 et
web-prod-02.
Convertir un type
Section intitulée « Convertir un type »CAST(valeur AS type), ou sa forme courte PostgreSQL valeur::type,
convertit une valeur : '42'::int + 1 vaut 43, '2026-04-01'::date est une
date. Une conversion impossible est une erreur, pas un NULL :
'abc'::int échoue avec invalid input syntax for type integer: "abc". Sous
SQLite, la même conversion rend 0 sans prévenir.
À retenir
Section intitulée « À retenir »- Une agrégation résume des lignes ;
count(*)compte les lignes,count(colonne)les valeurs présentes. - Avec
GROUP BY, chaque colonne duSELECTest groupée ou agrégée ; SQLite accepte l'inverse et affiche une valeur arbitraire. WHEREfiltre les lignes avant le regroupement,HAVINGles groupes après.- La division de deux entiers est entière : un taux se calcule avec
100.0 *en tête, etFILTERcompte un sous-ensemble. date_truncregroupe par période, la soustraction de deux dates donne uninterval;split_partetstring_aggdécoupent et assemblent les chaînes.
Contrôle de connaissances
Section intitulée « Contrôle de connaissances »Contrôle de connaissances
Validez vos connaissances avec ce quiz interactif
Informations
- Le chronomètre démarre au clic sur Démarrer
- Questions à choix multiples, vrai/faux et réponses courtes
- Vous pouvez naviguer entre les questions
- Les résultats détaillés sont affichés à la fin
Lance le quiz et démarre le chronomètre
Vérification
(0/0)Score par domaine
Un domaine regroupe plusieurs guides et reçoit plusieurs questions : son score est plus fiable qu'un score par thème, qui porte souvent sur une seule question.
Bilan
Points forts
Aucun sujet réussi à 80 % sur au moins deux questions pour l’instant.
Points faibles
Aucun sujet sous le seuil de réussite.
À faire ensuite
Des indices pour retenter votre chance ?
Continuer le cours : CASE, CTE et fenêtrage
Nouveau quiz complet avec des questions aléatoires
Retravailler uniquement les questions ratées
Retour à la liste des certifications