Requêtes SQL avec critères et fonctions d'agrégation
- Savoir filtrer les données avec des conditions simples ou multiples.
- Utiliser efficacement les fonctions d'agrégation (
COUNT,AVG,SUM, etc.). - Maîtriser les regroupements (
GROUP BY) et les filtres d'agrégats (HAVING). - Formuler des requêtes imbriquées simples sans utiliser de jointures.
Introduction
Le chapitre précédent a posé les bases du SQL : créer des tables, insérer des données, faire des sélections simples et des jointures. Ce chapitre va plus loin : on apprend à interroger finement les données avec des conditions élaborées, des calculs statistiques, et des requêtes qui s'appellent entre elles.
Ces techniques sont celles que vous utiliserez le plus en pratique pour analyser et exploiter des bases de données réelles.
La grammaire d'une requête SQL
SELECT colonnes
FROM table
[WHERE condition]
[GROUP BY colonne]
[HAVING condition]
[ORDER BY colonne]
[LIMIT n];
Chaque clause joue un rôle précis. Ce qui rend SQL particulier : l'ordre d'écriture n'est pas l'ordre d'exécution.
| Ordre | Clause | Rôle |
|---|---|---|
| 1 | FROM | Charge la table source de données |
| 2 | WHERE | Filtre les lignes avant tout calcul |
| 3 | GROUP BY | Regroupe les lignes ayant des colonnes identiques |
| 4 | HAVING | Filtre les groupes après GROUP BY |
| 5 | SELECT | Calcule et sélectionne les colonnes à afficher |
| 6 | DISTINCT | Supprime les doublons dans les résultats |
| 7 | ORDER BY | Trie les résultats affichés |
| 8 | LIMIT | Limite le nombre de lignes retournées |
FROM est traitée en premier, même si elle est écrite après SELECT.C'est pourquoi on ne peut pas utiliser un alias défini dans SELECT à l'intérieur d'un WHERE : quand
WHERE s'exécute, SELECT n'a pas encore calculé l'alias.En revanche,
ORDER BY s'exécute après SELECT, donc les alias y sont accessibles.Filtres avec WHERE
SELECT TitreFilm, AnneeSortie
FROM FILM
WHERE Pays = 'USA' AND AnneeSortie BETWEEN 2000 AND 2010;
Utilisation de plusieurs conditions avec AND, OR, BETWEEN, LIKE :
SELECT NomActeur, PrenomActeur
FROM ACTEURS
WHERE NomActeur LIKE 'Mac%' OR DateNaissanceActeur BETWEEN '1980-01-01' AND '1990-12-31';
Opérateurs disponibles dans WHERE
| Opérateur | Description | Exemple |
|---|---|---|
= | Egal a | Pays = 'USA' |
<> ou != | Different de | AnneeSortie <> 2023 |
>, <, >=, <= | Comparaison numerique ou alphabetique | Budget >= 1000000 |
BETWEEN ... AND | Valeur comprise dans un intervalle (bornes incluses) | AnneeSortie BETWEEN 2000 AND 2010 |
IN (...) | Appartenance a une liste | Pays IN ('USA', 'France', 'UK') |
NOT IN (...) | Exclusion d'une liste | Pays NOT IN ('Chine', 'Russie') |
LIKE | Correspondance partielle avec des jokers | TitreFilm LIKE 'Star%' |
IS NULL | Teste si une valeur est nulle | DateSortie IS NULL |
IS NOT NULL | Teste si une valeur est non nulle | DateSortie IS NOT NULL |
Utilisation de jokers avec LIKE
| Joker | Signification | Exemple |
|---|---|---|
% | Remplace n'importe quelle suite de caracteres (y compris vide) | 'Mac%' trouve MacDonald, Macbeth, Mac |
_ | Remplace exactement un seul caractere | 'M_c' trouve Mac, Mec, Mic |
-- Tous les films américains ou britanniques sortis après 2010
WHERE (Pays = 'USA' OR Pays = 'UK') AND AnneeSortie > 2010
Les parenthèses sont essentielles : sans elles, le AND étant prioritaire sur le OR, l'interprétation serait différente.
Le piège des valeurs NULL
NULL signifie "valeur inconnue". On ne peut pas comparer NULL avec = ou !=.-- ❌ Ne retourne RIEN, même si des lignes ont DateSortie = NULL
WHERE DateSortie = NULL
-- ✅ Correct
WHERE DateSortie IS NULL
-- ❌ N'inclut pas les lignes avec NULL dans DateSortie
WHERE DateSortie != '2020-01-01'
-- ✅ Inclut les NULL
WHERE DateSortie != '2020-01-01' OR DateSortie IS NULL
WHERE filtre les lignes individuelles avant tout calcul.Une bonne utilisation des operateurs permet d'extraire precisement les donnees voulues.
Fonctions d'agrégation
Les fonctions d'agrégation calculent une valeur à partir de plusieurs lignes. Elles sont incontournables pour produire des statistiques.
| Fonction | Role | Remarque |
|---|---|---|
COUNT(*) | Nombre total de lignes | Compte toutes les lignes, y compris les NULL |
COUNT(col) | Nombre de valeurs non NULL dans col | Ignore les NULL |
SUM(col) | Somme des valeurs | Ignore les NULL |
AVG(col) | Moyenne des valeurs | Ignore les NULL |
MIN(col) | Valeur minimale | Fonctionne aussi sur des dates et textes |
MAX(col) | Valeur maximale | Fonctionne aussi sur des dates et textes |
Exemples sur l'ensemble de la table
-- Nombre de films sortis en 2009
SELECT COUNT(*) AS nb_films
FROM FILM
WHERE AnneeSortie = 2009;
-- Plus petit et plus grand budget de la base
SELECT MIN(Budget) AS budget_min, MAX(Budget) AS budget_max
FROM FILM;
-- Budget moyen arrondi des films américains
SELECT ROUND(AVG(Budget), 2) AS budget_moyen
FROM FILM
WHERE Pays = 'USA';
Regrouper avec GROUP BY
GROUP BY divise les lignes en groupes selon une colonne, et les fonctions d'agrégation s'appliquent à chaque groupe séparément.
Table FILM (extrait) :
| TitreFilm | Pays | Budget |
|---|---|---|
| Inception | USA | 160000000 |
| Interstellar | USA | 165000000 |
| Parasite | Coree | 11400000 |
| Titanic | USA | 200000000 |
| Okja | Coree | 50000000 |
SELECT Pays, COUNT(*) AS nb_films, AVG(Budget) AS budget_moyen
FROM FILM
GROUP BY Pays;
Résultat :
| Pays | nb_films | budget_moyen |
|---|---|---|
| USA | 3 | 175000000 |
| Coree | 2 | 30700000 |
Chaque groupe (USA, Coree) produit une seule ligne de résultat. Les 5 lignes d'origine sont résumées en 2.
-- Budget total cumulé par année
SELECT AnneeSortie, SUM(Budget) AS total_budget
FROM FILM
GROUP BY AnneeSortie;
SELECT avec GROUP BY, chaque colonne affichée doit être soit :- dans la clause
GROUP BY, soit une fonction d'agrégation (COUNT, AVG, etc.)Sinon, la requête est invalide (ou retourne un résultat imprévisible selon le SGBD).
SELECT.Elles calculent une valeur unique par groupe ou sur l'ensemble des lignes.
Pour les appliquer à plusieurs groupes, utilisez
GROUP BY.Pour filtrer les résultats agrégés, utilisez
HAVING (pas WHERE).Filtrer après regroupement : HAVING
HAVING s'applique après GROUP BY pour filtrer les groupes selon une condition sur les agrégats.
-- Pays ayant produit plus de 5 films
SELECT Pays, COUNT(*) AS nb
FROM FILM
GROUP BY Pays
HAVING nb > 5;
-- Années dont le budget cumulé dépasse 500 millions
SELECT AnneeSortie, SUM(Budget) AS total_budget
FROM FILM
GROUP BY AnneeSortie
HAVING total_budget > 500000000;
Combiner WHERE et HAVING
Les deux clauses peuvent coexister : elles n'agissent pas au même moment.
-- Années avec au moins 3 films américains et un budget moyen supérieur à 10M
SELECT AnneeSortie, COUNT(*) AS nb_films, AVG(Budget) AS budget_moyen
FROM FILM
WHERE Pays = 'USA' -- 1. filtre les lignes (films USA seulement)
GROUP BY AnneeSortie -- 2. groupe par année
HAVING nb_films >= 3 -- 3. filtre les groupes
AND AVG(Budget) > 10000000;
Différence entre WHERE et HAVING
WHERE | HAVING | |
|---|---|---|
| Quand | Avant le regroupement | Après le GROUP BY |
| Sur quoi | Les lignes individuelles | Les groupes |
| Agrégats autorisés | Non | Oui (COUNT, AVG, etc.) |
-- ❌ Erreur : AVG ne peut pas être utilisé dans WHERE
SELECT Pays FROM FILM
WHERE AVG(Budget) > 10000000;
-- ✅ Correct
SELECT Pays FROM FILM
GROUP BY Pays
HAVING AVG(Budget) > 10000000;
WHERE pour filtrer les lignes individuelles (avant regroupement).Utilisez
HAVING pour filtrer les groupes formés par GROUP BY.Les deux peuvent être combinés dans une même requête - ils s'appliquent à des moments différents.
Requêtes imbriquées (sous-requêtes)
Une sous-requête est un SELECT placé à l'intérieur d'une autre requête. Elle est exécutée en premier, et son résultat est utilisé par la requête externe.
- Scalaire : retourne une seule valeur - utilisée avec
=,>,<... - Multi-valeur : retourne une colonne de valeurs - utilisée avec
IN/NOT IN - Corrélée : fait référence à la requête externe, réévaluée ligne par ligne
Sous-requêtes scalaires
-- Films dont le budget est supérieur à la moyenne
SELECT TitreFilm, Budget
FROM FILM
WHERE Budget > (
SELECT AVG(Budget) FROM FILM
);
-- Film(s) ayant le budget maximum
SELECT TitreFilm, Budget
FROM FILM
WHERE Budget = (
SELECT MAX(Budget) FROM FILM
);
-- Films sortis la même année qu'Inception
SELECT TitreFilm
FROM FILM
WHERE AnneeSortie = (
SELECT AnneeSortie FROM FILM WHERE TitreFilm = 'Inception'
);
=, >, etc. doit retourner exactement une valeur.Si elle retourne plusieurs lignes, le SGBD retourne une erreur.
Utilisez
MAX(), MIN(), AVG() pour garantir un résultat unique.Sous-requêtes avec IN et NOT IN
-- Acteurs plus jeunes que le plus ancien enregistré
SELECT NomActeur, PrenomActeur
FROM ACTEURS
WHERE DateNaissanceActeur > (
SELECT MIN(DateNaissanceActeur) FROM ACTEURS
);
-- Films dont le budget correspond à celui d'un film de 2020
SELECT TitreFilm
FROM FILM
WHERE Budget IN (
SELECT Budget FROM FILM WHERE AnneeSortie = 2020
);
-- Films dont le titre n'existe pas en 2010
SELECT TitreFilm
FROM FILM
WHERE TitreFilm NOT IN (
SELECT TitreFilm FROM FILM WHERE AnneeSortie = 2010
);
NULL, NOT IN ne retournera aucune ligne.C'est un comportement logiquement cohérent mais contre-intuitif.
En cas de doute, préférez
NOT EXISTS qui gère correctement les NULL.Requêtes avancées sans jointure
Nombre de films par année (seulement si >= 3)
SELECT AnneeSortie, COUNT(*) AS nb
FROM FILM
GROUP BY AnneeSortie
HAVING nb >= 3
ORDER BY AnneeSortie;
Top 3 des années avec le plus gros budget cumulé
SELECT AnneeSortie, SUM(Budget) AS total
FROM FILM
GROUP BY AnneeSortie
ORDER BY total DESC
LIMIT 3;
Film le plus cher de chaque année (sous-requête corrélée)
SELECT f1.TitreFilm, f1.AnneeSortie, f1.Budget
FROM FILM f1
WHERE f1.Budget = (
SELECT MAX(f2.Budget)
FROM FILM f2
WHERE f2.AnneeSortie = f1.AnneeSortie
);
La sous-requête est corrélée : elle est réévaluée pour chaque ligne de f1, en cherchant le budget maximum de la même année.
Réalisateurs ayant sorti plus de films que la moyenne
SELECT NomRealisateur, PrenomRealisateur, COUNT(*) AS nb_films
FROM FILM
GROUP BY NomRealisateur, PrenomRealisateur
HAVING nb_films > (
SELECT AVG(nb)
FROM (
SELECT COUNT(*) AS nb
FROM FILM
GROUP BY NomRealisateur, PrenomRealisateur
) AS sous_stats
);
Elle se lit de l'intérieur vers l'extérieur :
- La sous-requête la plus interne calcule le nombre de films par réalisateur.
- La sous-requête intermédiaire calcule la moyenne de ces nombres.
- La requête externe ne garde que les réalisateurs dépassant cette moyenne.
Opérateurs ALL et ANY
-- Films strictement plus chers que TOUS les autres films de leur année
SELECT f1.TitreFilm, f1.AnneeSortie, f1.Budget
FROM FILM f1
WHERE f1.Budget > ALL (
SELECT f2.Budget
FROM FILM f2
WHERE f2.AnneeSortie = f1.AnneeSortie AND f2.TitreFilm <> f1.TitreFilm
);
-- Films plus chers qu'AU MOINS UN film de 2010
SELECT TitreFilm, Budget
FROM FILM
WHERE Budget > ANY (
SELECT Budget FROM FILM WHERE AnneeSortie = 2010
);
ALL : la condition doit être vraie pour tous les éléments de la sous-requête.ANY (ou SOME) : la condition doit être vraie pour au moins un élément.= ANY (...) est équivalent à IN (...).GROUP BY, HAVING, ORDER BY et sous-requêtes.Les sous-requêtes corrélées permettent de croiser des données entre lignes sans jointure explicite.
Les opérateurs
ALL et ANY permettent des comparaisons ensemblistes puissantes.Tri des résultats : ORDER BY
-- Films américains triés par budget décroissant
SELECT TitreFilm, AnneeSortie, Budget
FROM FILM
WHERE Pays = 'USA'
ORDER BY Budget DESC;
Trier par plusieurs colonnes
-- D'abord par année croissante, puis par budget décroissant dans chaque année
SELECT TitreFilm, AnneeSortie, Budget
FROM FILM
ORDER BY AnneeSortie ASC, Budget DESC;
L'ordre de tri est appliqué colonne par colonne : si deux films ont la même année, le budget départage.
Trier après agrégation
-- Années triées par nombre de films décroissant
SELECT AnneeSortie, COUNT(*) AS nb_films
FROM FILM
GROUP BY AnneeSortie
ORDER BY nb_films DESC;
On peut utiliser l'alias défini dans SELECT directement dans ORDER BY (car ORDER BY s'exécute après SELECT).
Trier les valeurs NULL
Le comportement par défaut des NULL varie selon le SGBD. Pour forcer leur position en MySQL :
-- Dates connues en premier, NULL en dernier
ORDER BY DateSortie IS NULL ASC, DateSortie ASC;
DateSortie IS NULL retourne 0 (faux) pour les valeurs renseignées et 1 (vrai) pour les NULL. En triant ASC, les 0 viennent avant les 1.
- Utilisez
ORDER BYpour faciliter la lecture et la hiérarchisation des résultats. - Combinez
GROUP BY,HAVING, etORDER BYpour construire des requêtes puissantes. - Utilisez toujours des aliases lisibles dans
SELECT, surtout quand vous triez sur des agrégats. ASC(croissant) est le comportement par défaut - ne l'écrivez que si vous voulez être explicite.
