Différence entre WHERE et HAVING en SQL
Deux filtres, deux moments — quand utiliser WHERE, quand utiliser HAVING, et pourquoi
SECTION 01
WHERE : filtrer les lignes
WHERE filtre les lignes individuelles de la table. Il s’exécute avant GROUP BY — les lignes éliminées ne participent pas au calcul des agrégats.
— id | vendeur | produit | montant | date
— 1 | Alice | Laptop | 999 | 2025-01-15
— 2 | Bob | Clavier | 79 | 2025-01-20
— 3 | Alice | Souris | 29 | 2025-02-10
— 4 | Charlie | Casque | 149 | 2025-02-12
— 5 | Bob | Écran | 349 | 2025-03-01
— 6 | Alice | Clavier | 79 | 2025-03-15— Ventes supérieures à 100 €
SELECT * FROM ventes
WHERE montant > 100;
— Lignes 1, 4, 5 (trois lignes individuelles)
— Ventes de janvier
SELECT * FROM ventes
WHERE date >= ‘2025-01-01’ AND date < ‘2025-02-01’;
— Ventes de Bob
SELECT * FROM ventes
WHERE vendeur = ‘Bob’;
WHERE travaille sur des valeurs individuelles des colonnes : montant d’une vente, nom d’un vendeur, date d’une commande. Il ne peut jamais contenir COUNT, SUM, AVG ou tout autre agrégat.
SECTION 02
HAVING : filtrer les groupes
HAVING filtre les résultats des fonctions d’agrégation. Il s’exécute après GROUP BY — il élimine des groupes entiers, pas des lignes individuelles.
SELECT vendeur, COUNT(*) AS nb_ventes
FROM ventes
GROUP BY vendeur
HAVING COUNT(*) > 2;— vendeur | nb_ventes
— Alice | 3 ← seul résultat (Bob=2, Charlie=1)
— Vendeurs avec un CA total supérieur à 200 €
SELECT vendeur, SUM(montant) AS ca
FROM ventes
GROUP BY vendeur
HAVING SUM(montant) > 200;
— vendeur | ca
— Alice | 1107
— Bob | 428
HAVING n’a de sens qu’avec GROUP BY. Sans regroupement, il n’y a pas de « groupe » à filtrer. Si tu n’as pas de GROUP BY, utilise WHERE.
SECTION 03
Tableau comparatif complet
| Critère | WHERE | HAVING |
|---|---|---|
| Filtre sur | Lignes individuelles | Groupes (résultats d’agrégation) |
| Moment d’exécution | Avant GROUP BY | Après GROUP BY |
| Agrégats autorisés | ❌ Non (COUNT, SUM…) | ✅ Oui |
| Colonnes normales | ✅ Oui | ✅ Oui (mais préférer WHERE) |
| Nécessite GROUP BY | ❌ Non | ✅ Oui (logiquement) |
| Performance | ✅ Plus rapide (filtre tôt) | ⚠️ Plus lent (filtre tard) |
| Peut utiliser un alias | ❌ Non (s’exécute avant SELECT) | ⚠️ Dépend du SGBD (MySQL oui, PG non) |
SECTION 04
WHERE + HAVING ensemble
En pratique, tu combines souvent WHERE (pré-filtre les données) et HAVING (filtre les résultats agrégés) :
— « Vendeurs ayant vendu plus de 500 € au 1er trimestre 2025 »
SELECT vendeur, SUM(montant) AS ca
FROM ventes
WHERE date >= ‘2025-01-01’ — 1. Filtre les LIGNES (Q1 seulement)
AND date < ‘2025-04-01’
GROUP BY vendeur — 2. Regroupe par vendeur
HAVING SUM(montant) > 500 — 3. Filtre les GROUPES (CA > 500)
ORDER BY ca DESC; — 4. Trie
— vendeur | ca
— Alice | 1107
— Résultat : 6 lignes (toutes nos ventes sont en Q1)— Étape 2 — GROUP BY regroupe par vendeur
— Alice : 999 + 29 + 79 = 1107
— Bob : 79 + 349 = 428
— Charlie : 149
— Étape 3 — HAVING élimine les groupes avec CA <= 500
— ❌ Bob (428) — éliminé
— ❌ Charlie (149) — éliminé
— ✅ Alice (1107) — gardé
— Étape 4 — ORDER BY trie (un seul résultat ici)
Règle d’or : tout ce qui peut être filtré avec WHERE doit l’être. WHERE réduit les données avant le groupement = moins de lignes à agréger = requête plus rapide. HAVING ne sert que pour ce qui ne peut pas être filtré avant : les résultats d’agrégation.
SECTION 05
Même question, deux requêtes différentes
SELECT vendeur, SUM(montant) AS ca
FROM ventes
GROUP BY vendeur
HAVING MIN(date) >= ‘2025-01-01’;— ⚠️ Problèmes :
— 1. Regroupe TOUTES les lignes d’abord (inutilement)
— 2. Le filtre ne fait pas ce que tu veux (MIN date ≠ toutes les dates)
— 3. Plus lent sur de grandes tables
SELECT vendeur, SUM(montant) AS ca
FROM ventes
WHERE date >= ‘2025-01-01’ AND date < ‘2025-04-01’
GROUP BY vendeur;
| Question à se poser | Réponse |
|---|---|
| Le filtre porte sur une valeur de colonne brute ? | → WHERE |
| Le filtre porte sur un résultat d’agrégat (COUNT, SUM, AVG) ? | → HAVING |
| Le filtre peut s’appliquer avant le GROUP BY ? | → WHERE (plus performant) |
| Le filtre nécessite que le GROUP BY soit calculé ? | → HAVING |
| Pas de GROUP BY dans la requête ? | → WHERE (toujours) |
SECTION 06
Erreurs fréquentes
| Erreur | Ce qui se passe | Correction |
|---|---|---|
| WHERE COUNT(*) > 5 | Erreur de syntaxe — agrégat interdit dans WHERE | HAVING COUNT(*) > 5 |
| Filtrer une colonne avec HAVING | Fonctionne mais lent — groupe tout d’abord | Utiliser WHERE pour les colonnes brutes |
| HAVING total > 100 sur PostgreSQL | Erreur — alias non reconnu dans HAVING | HAVING SUM(montant) > 100 |
| HAVING sans GROUP BY | Techniquement possible (traite la table entière comme un groupe) | Généralement un bug — utiliser WHERE |
SECTION 07
Questions fréquentes
📊 GROUP BY & HAVING
🔢 Fonctions d’agrégation
🔗 Jointures SQL
🔍 Sous-requêtes
📑 ORDER BY & LIMIT
🗄️ Cours SQL complet
🏠 Hub Programmation
Différence entre WHERE et HAVING en SQL
Référence : sql.sh HAVING
