Fonctions d’Agrégation SQL : COUNT, SUM, AVG, MIN, MAX
Compter, additionner, moyenner, trouver le min et le max — avec et sans GROUP BY
Les données d’exemple
— id | vendeur | categorie | montant | date
— 1 | Alice | Tech | 999 | 2025-01-15
— 2 | Bob | Tech | 79 | 2025-01-20
— 3 | Alice | Culture | 25 | 2025-02-10
— 4 | Charlie | Tech | 149 | 2025-02-12
— 5 | Bob | Tech | 349 | 2025-03-01
— 6 | Alice | Culture | 15 | 2025-03-15
— 7 | Bob | Culture | NULL | 2025-03-20 ← montant manquant
La ligne 7 a un montant NULL. C’est volontaire — les agrégats gèrent les NULL différemment selon la fonction. C’est un piège classique en SQL.
COUNT — compter les lignes
SELECT COUNT(*) FROM ventes;
— 7
— COUNT(colonne) — compte les lignes NON NULL
SELECT COUNT(montant) FROM ventes;
— 6 (la ligne 7 avec montant NULL est ignorée)
— COUNT(DISTINCT colonne) — compte les valeurs uniques
SELECT COUNT(DISTINCT vendeur) FROM ventes;
— 3 (Alice, Bob, Charlie)
SELECT COUNT(DISTINCT categorie) FROM ventes;
— 2 (Tech, Culture)
| Syntaxe | Compte | Ignore les NULL |
|---|---|---|
| COUNT(*) | Toutes les lignes | ❌ Non (compte tout) |
| COUNT(colonne) | Lignes où colonne n’est pas NULL | ✅ Oui |
| COUNT(DISTINCT col) | Valeurs uniques non NULL | ✅ Oui |
Règle simple : utilise COUNT(*) pour compter le nombre de lignes. Utilise COUNT(colonne) quand tu veux exclure les NULL. Utilise COUNT(DISTINCT) pour les valeurs uniques.
SUM — additionner
SELECT SUM(montant) FROM ventes;
— 1616 (les NULL sont ignorés)
— Total par vendeur
SELECT vendeur, SUM(montant) AS total
FROM ventes
GROUP BY vendeur;
— vendeur | total
— Alice | 1039
— Bob | 428 (79 + 349, le NULL est ignoré)
— Charlie | 149
— Total des ventes Tech uniquement
SELECT SUM(montant) AS total_tech
FROM ventes
WHERE categorie = ‘Tech’;
— 1576
SUM ne fonctionne que sur les colonnes numériques. Il ignore les NULL. Si toutes les valeurs sont NULL, SUM retourne NULL (pas 0). Pour obtenir 0, utilise COALESCE(SUM(montant), 0).
AVG — moyenne
SELECT AVG(montant) FROM ventes;
— 269.33 (1616 / 6 — le NULL est EXCLU du calcul)
— ⚠️ PIÈGE : AVG ignore les NULL
— Moyenne = SUM(non null) / COUNT(non null)
— Pas SUM / COUNT(*)
— Moyenne par catégorie
SELECT categorie,
AVG(montant) AS moy,
COUNT(*) AS nb_total,
COUNT(montant) AS nb_non_null
FROM ventes
GROUP BY categorie;
— categorie | moy | nb_total | nb_non_null
— Tech | 394.00 | 4 | 4
— Culture | 20.00 | 3 | 2 ← ligne 7 ignorée par AVG
— Arrondir la moyenne
SELECT ROUND(AVG(montant), 2) AS moyenne_arrondie
FROM ventes;
⚠️ Piège majeur : AVG divise par le nombre de valeurs non NULL, pas par le nombre total de lignes. Si tu veux que les NULL comptent comme 0 dans la moyenne, utilise AVG(COALESCE(montant, 0)).
MIN / MAX — valeur min et max
SELECT MIN(montant) AS min_vente,
MAX(montant) AS max_vente
FROM ventes;
— min_vente: 15 | max_vente: 999
— Première et dernière vente (sur les dates)
SELECT MIN(date) AS premiere,
MAX(date) AS derniere
FROM ventes;
— premiere: 2025-01-15 | derniere: 2025-03-20
— MIN/MAX sur du texte (ordre alphabétique)
SELECT MIN(vendeur), MAX(vendeur) FROM ventes;
— Alice, Charlie
— Min et Max par vendeur
SELECT vendeur,
MIN(montant) AS plus_petite,
MAX(montant) AS plus_grande
FROM ventes
GROUP BY vendeur;
MIN et MAX fonctionnent sur les nombres, les dates et le texte (ordre alphabétique). Comme les autres agrégats, ils ignorent les NULL.
Combiner plusieurs agrégats
SELECT
COUNT(*) AS nb_ventes,
COUNT(DISTINCT vendeur) AS nb_vendeurs,
SUM(montant) AS ca_total,
ROUND(AVG(montant), 2) AS panier_moyen,
MIN(montant) AS vente_min,
MAX(montant) AS vente_max
FROM ventes;
— nb_ventes | nb_vendeurs | ca_total | panier_moyen | vente_min | vente_max
— 7 | 3 | 1616 | 269.33 | 15 | 999
— Agrégats conditionnels (MySQL / PostgreSQL)
SELECT
SUM(CASE WHEN categorie = ‘Tech’ THEN montant ELSE 0 END) AS ca_tech,
SUM(CASE WHEN categorie = ‘Culture’ THEN montant ELSE 0 END) AS ca_culture,
COUNT(CASE WHEN montant > 100 THEN 1 END) AS grosses_ventes
FROM ventes;
— PostgreSQL : FILTER (plus lisible)
SELECT
SUM(montant) FILTER (WHERE categorie = ‘Tech’) AS ca_tech,
SUM(montant) FILTER (WHERE categorie = ‘Culture’) AS ca_culture
FROM ventes;
Le pattern SUM(CASE WHEN … THEN … END) permet de faire des agrégats conditionnels — calculer des totaux par catégorie sans GROUP BY. C’est l’équivalent SQL d’un tableau croisé dynamique.
Agrégats + GROUP BY
SELECT vendeur,
COUNT(*) AS nb_ventes,
SUM(montant) AS ca,
ROUND(AVG(montant), 2) AS panier_moyen,
MAX(montant) AS meilleure_vente
FROM ventes
GROUP BY vendeur
HAVING SUM(montant) > 100
ORDER BY ca DESC;
— vendeur | nb_ventes | ca | panier_moyen | meilleure_vente
— Alice | 3 | 1039 | 346.33 | 999
— Bob | 3 | 428 | 214.00 | 349
— Charlie | 1 | 149 | 149.00 | 149
— CA mensuel
SELECT DATE_FORMAT(date, ‘%Y-%m’) AS mois, — MySQL
— SELECT TO_CHAR(date, ‘YYYY-MM’) AS mois, — PostgreSQL
SUM(montant) AS ca,
COUNT(*) AS nb_ventes
FROM ventes
GROUP BY mois
ORDER BY mois;
Erreurs fréquentes
| Erreur | Problème | Solution |
|---|---|---|
| COUNT(*) avec LEFT JOIN | Compte 1 même sans correspondance | Utiliser COUNT(table.colonne) |
| AVG ignore les NULL | Moyenne biaisée (divise par le nb de non-null) | AVG(COALESCE(col, 0)) si les NULL = 0 |
| SUM retourne NULL si tout est NULL | Résultat inattendu | COALESCE(SUM(col), 0) |
| Agrégat dans le WHERE | WHERE SUM(montant) > 100 → erreur | Utiliser HAVING |
| Oublier GROUP BY | Agrégat sur toute la table au lieu de par groupe | Ajouter GROUP BY pour des résultats par catégorie |
| SELECT colonne non groupée | Colonne absente de GROUP BY / agrégat | L’ajouter au GROUP BY ou dans un agrégat |
Questions fréquentes
📊 GROUP BY & HAVING
⚖️ WHERE vs HAVING
🔗 Jointures SQL
🔍 Sous-requêtes
📑 ORDER BY & LIMIT
🗄️ Cours SQL complet
🏠 Hub Programmation
Fonctions d’agrégation SQL — COUNT, SUM, AVG, MIN, MAX
Référence : sql.sh Fonctions d’agrégation
