SQL avancé
Objectifs du Chapitre
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.
Les requêtes simples suffisent tant qu'on interroge une table. Dès qu'il faut croiser, regrouper, comparer un résultat à un autre résultat, il faut savoir dans quel ordre le moteur travaille : WHERE avant le regroupement, HAVING après. Ce chapitre traite ce qui fait vraiment trébucher : les NULL, la différence WHERE / HAVING, et les sous-requêtes.
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 |
La clause 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
Utilisation de plusieurs conditions avec AND, OR, BETWEEN, LIKE :
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 |
Combiner plusieurs conditions avec des parenthèses
-- 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
En SQL, 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
La clause 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
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.
Visualiser l'effet de GROUP BY
Table FILM (extrait) :
| TitreFilm | Pays | Budget |
|---|---|---|
| Inception | USA | 160000000 |
| Interstellar | USA | 165000000 |
| Parasite | Coree | 11400000 |
| Titanic | USA | 200000000 |
| Okja | Coree | 50000000 |
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.
Règle fondamentale du GROUP BY
Dans un 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).
Les fonctions d'agrétion s'utilisent dans la clause 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.
Combiner WHERE et HAVING
Les deux clauses peuvent coexister : elles n'agissent pas au même moment.
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.) |
Exécute le bloc suivant : le moteur refuse la requête, et son message est exactement la leçon à retenir.
La bonne écriture regroupe d'abord, puis filtre sur le résultat du regroupement :
Utilisez 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.
Les trois types de sous-requêtes
- 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
Une sous-requête utilisée avec =, >, 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
Si la sous-requête retourne au moins un 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)
Top 3 des années avec le plus gros budget cumulé
Film le plus cher de chaque année (sous-requête corrélée)
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
Comment lire cette requête complexe
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
);
ALL et ANY sont au standard SQL et fonctionnent sous MySQL et PostgreSQL, mais le moteur du navigateur (SQLite) ne les connaît pas. On les réécrit sans rien perdre : « supérieur à TOUS » revient à « supérieur au maximum », et « supérieur à AU MOINS UN » à « supérieur au minimum ».
-- 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 vs ANY
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 (...).
Les requêtes avancées peuvent combiner 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
Trier par plusieurs colonnes
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
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.