Fonctions d’Agrégation SQL : COUNT, SUM, AVG, MIN, MAX

Compter, additionner, moyenner, trouver le min et le max — avec et sans GROUP BY

9
Sections
25+
Exemples
SQL
Standard

SECTION 01

Les données d’exemple

— Table ventes
— 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.

SECTION 02

COUNT — compter les lignes

— COUNT(*) — compte TOUTES les lignes (y compris les NULL)
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.

SECTION 03

SUM — additionner

— Total des ventes
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).

SECTION 04

AVG — moyenne

— Montant moyen d’une vente
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)).

SECTION 05

MIN / MAX — valeur min et max

— Plus petite et plus grande vente
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.

SECTION 06

Combiner plusieurs agrégats

📊 Dashboard en une requête
— Statistiques globales
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.

SECTION 07

Agrégats + GROUP BY

— Tableau de bord par vendeur
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;

SECTION 08

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

SECTION 09

Questions fréquentes

COUNT(*) vs COUNT(1) vs COUNT(colonne) ?
COUNT(*) et COUNT(1) sont identiques en performance et résultat — ils comptent toutes les lignes. COUNT(colonne) ne compte que les lignes où la colonne n’est pas NULL. Les optimiseurs modernes traitent COUNT(*) et COUNT(1) de manière équivalente.
Comment calculer un pourcentage ?
Utilise une sous-requête ou un agrégat conditionnel : ROUND(100.0 * COUNT(CASE WHEN statut = ‘payee’ THEN 1 END) / COUNT(*), 2) AS pct_payees. Note le 100.0 (pas 100) pour forcer la division décimale.
SUM peut-il additionner des colonnes entre elles ?
Non, SUM additionne les valeurs d’une même colonne sur plusieurs lignes. Pour additionner deux colonnes sur la même ligne, utilise l’opérateur + : SELECT prix + taxe AS total FROM produits.
Comment trouver la ligne (pas juste la valeur) du MAX ?
SELECT * FROM ventes ORDER BY montant DESC LIMIT 1 retourne toute la ligne. Ou avec une sous-requête : WHERE montant = (SELECT MAX(montant) FROM ventes). En PostgreSQL, tu peux utiliser DISTINCT ON avec ORDER BY.

Fonctions d’agrégation SQL — COUNT, SUM, AVG, MIN, MAX

Référence : sql.sh Fonctions d’agrégation