Différence entre WHERE et HAVING en SQL

Deux filtres, deux moments — quand utiliser WHERE, quand utiliser HAVING, et pourquoi

7
Sections
15+
Exemples
SQL
Standard

SECTION 01

WHERE : filtrer les lignes

🔍 Avant le regroupement

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.

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

📊 Après le regroupement

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.

— Vendeurs ayant réalisé plus de 2 ventes
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

🎯 Le cas le plus courant en entreprise

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

📋 Étape par étape
— Étape 1 — WHERE filtre les lignes du Q1
— 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

🔴 Mauvais — filtrer la date avec HAVING
— ❌ Techniquement possible en MySQL, mais MAUVAISE PRATIQUE
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

🟢 Bon — filtrer la date avec WHERE
— ✅ WHERE filtre d’abord, GROUP BY travaille sur moins de lignes
SELECT vendeur, SUM(montant) AS ca
FROM ventes
WHERE date >= ‘2025-01-01’ AND date < ‘2025-04-01’
GROUP BY vendeur;
🧠 Aide-mémoire : WHERE ou HAVING ?
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

Peut-on utiliser WHERE et HAVING dans la même requête ?
Oui, et c’est même la bonne pratique. WHERE filtre les lignes avant le regroupement (réduit le volume de données), puis HAVING filtre les groupes après le calcul des agrégats. Les deux sont complémentaires.
HAVING est-il toujours plus lent que WHERE ?
Non, mais pour les filtres qui pourraient être dans WHERE, oui. WHERE élimine des lignes avant le GROUP BY, donc le moteur SQL travaille sur moins de données. HAVING filtre après le calcul — les agrégats sont calculés sur toutes les lignes, puis certains groupes sont jetés. Pour les filtres sur agrégats, HAVING est le seul choix.
HAVING fonctionne-t-il sans GROUP BY ?
Oui, techniquement. Sans GROUP BY, toute la table est considérée comme un seul groupe. SELECT COUNT(*) FROM ventes HAVING COUNT(*) > 10 retourne le count seulement s’il dépasse 10. Mais c’est rare et peu lisible — utilise une sous-requête ou un WHERE classique si possible.
Résumé en une phrase ?
WHERE filtre les lignes avant GROUP BY. HAVING filtre les groupes après GROUP BY. Si tu n’as pas besoin d’un agrégat dans la condition, utilise WHERE.

Différence entre WHERE et HAVING en SQL

Référence : sql.sh HAVING