J’aimerais revenir encore une fois sur le sujet des sous-requêtes dans SELECT. Surtout sur le fait que la requête interne peut faire référence aux données de la requête externe. Ça a l’air simple, mais en vrai, pas tant que ça. Plongeons encore un peu plus dans ce sujet...
Les sous-requêtes dans SELECT permettent d’ajouter des colonnes supplémentaires avec des valeurs calculées ou des données qui dépendent d’autres enregistrements ou tables. Par exemple, tu peux afficher la liste des étudiants avec leur moyenne, le nombre de cours auxquels ils sont inscrits, ou la note maximale actuelle dans le groupe. C’est super utile quand tu veux analyser les données "à la volée", en créant des colonnes récapitulatives sans prétraitement.
Bases des sous-requêtes dans SELECT
Avant de passer aux exemples, voyons la syntaxe générale. Les sous-requêtes dans SELECT ressemblent à ça :
SELECT column1,
column2,
(SELECT agrégation_ou_condition FROM autre_table WHERE condition) AS nouveau_nom_colonne
FROM table_principale;
Fais gaffe, la sous-requête retourne une seule valeur, qui apparaît dans le résultat comme une nouvelle colonne. Et le condition peut faire référence aux colonnes de la table_principale.
Exemple 1 : Ajouter la moyenne d’un étudiant
On commence simple et utile : on a une table students et une table grades où sont stockées les notes des étudiants.
Table students :
| id | name |
|---|---|
| 1 | Alex Lin |
| 2 | Anna Song |
| 3 | Dan Seth |
Table grades :
| student_id | grade |
|---|---|
| 1 | 90 |
| 1 | 85 |
| 2 | 76 |
| 3 | 88 |
| 3 | 92 |
Maintenant, on veut la liste des étudiants avec leur nom et leur moyenne. Pour ça, on utilise une sous-requête dans SELECT :
SELECT
s.id,
s.name,
(SELECT AVG(g.grade)
FROM grades g
WHERE g.student_id = s.id) AS average_grade
FROM students s;
Résultat :
| id | name | average_grade |
|---|---|---|
| 1 | Alex Lin | 87.5 |
| 2 | Anna Song | 76.0 |
| 3 | Dan Seth | 90.0 |
Ici, la sous-requête (SELECT AVG(g.grade) FROM grades g WHERE g.student_id = s.id) calcule la moyenne pour chaque étudiant. Elle retourne une valeur pour chaque ligne de la table students, et c’est pratique quand t’as pas envie de faire un JOIN ou de préparer des vues à l’avance.
Exemple 2 : Compter le nombre de cours pour chaque étudiant
Maintenant, ajoutons des infos sur les étudiants : combien de cours ils suivent. Pour ça, on a des tables en plus :
Table enrollments :
| student_id | course_id |
|---|---|
| 1 | 101 |
| 1 | 102 |
| 2 | 101 |
On affiche la liste des étudiants avec le nombre de cours auxquels ils sont inscrits :
SELECT
s.id,
s.name,
(SELECT COUNT(*)
FROM enrollments e
WHERE e.student_id = s.id) AS course_count -- référence à la table students de la requête externe
FROM students s;
Résultat :
| id | name | course_count |
|---|---|---|
| 1 | Alex Lin | 2 |
| 2 | Anna Song | 1 |
| 3 | Dan Seth | 0 |
La sous-requête (SELECT COUNT(*) FROM enrollments e WHERE e.student_id = s.id) compte le nombre d’enregistrements dans la table enrollments pour chaque étudiant.
Agrégation des données dans les sous-requêtes
Souvent, les sous-requêtes dans SELECT servent à calculer des données agrégées. Des fonctions comme AVG, SUM, COUNT, MAX, MIN permettent de traiter les données directement dans d’autres requêtes.
Exemple 3 : Score total de l’étudiant
Ajoutons le score total pour chaque étudiant. On va utiliser une sous-requête qui fait la somme de toutes les notes de la table grades :
SELECT
s.id,
s.name,
(SELECT SUM(g.grade)
FROM grades g
WHERE g.student_id = s.id) AS total_grade
FROM students s;
Résultat :
| id | name | total_grade |
|---|---|---|
| 1 | Alex Lin | 175 |
| 2 | Anna Song | 76 |
| 3 | Dan Seth | 180 |
Cette sous-requête (SELECT SUM(g.grade) FROM grades g WHERE g.student_id = s.id) additionne les notes de chaque étudiant. Si un étudiant n’a pas de notes, le résultat sera NULL, car SUM retourne NULL s’il n’y a pas de valeurs.
Limites et conseils
- Performance. Les sous-requêtes dans
SELECTsont exécutées séparément pour chaque ligne de la table principale. Ça peut ralentir grave sur de gros volumes de données. Si tu peux, remplace-les par desJOINou utilise des données agrégées préparées à l’avance. Par exemple :
SELECT
s.id,
s.name,
g.total_grade
FROM students s
LEFT JOIN (
SELECT student_id, SUM(grade) AS total_grade
FROM grades
GROUP BY student_id
) g ON s.id = g.student_id;
Cette approche avec JOIN est plus optimale, car le groupement et le calcul se font une seule fois.
2. Problèmes avec NULL.
Si la sous-requête ne trouve pas de données, le résultat sera NULL. Ça peut surprendre. Exemple :
SELECT
s.id,
s.name,
(SELECT SUM(g.grade)
FROM grades g
WHERE g.student_id = s.id) AS total_grade
FROM students s;
Si un étudiant n’a pas d’enregistrements dans grades, le résultat total_grade sera NULL. Pour remplacer NULL par 0, utilise la fonction COALESCE :
SELECT
s.id,
s.name,
COALESCE((SELECT SUM(g.grade)
FROM grades g
WHERE g.student_id = s.id), 0) AS total_grade
FROM students s;
Ouais, ici comme premier paramètre de la fonction COALESCE on passe
(
SELECT SUM(g.grade)
FROM grades g
WHERE g.student_id = s.id
)
Optimiser les sous-requêtes dans SELECT
Pour éviter les calculs inutiles et booster les perfs :
- Utilise des index sur les colonnes utilisées dans les sous-requêtes. Par exemple, indexer
student_iddans la tablegradesaccélère le filtrage. - Remplace les sous-requêtes par des données agrégées préparées avec
JOINsi possible. - Limite la quantité de données traitées par les sous-requêtes en filtrant (
WHERE).
Exemple final : combiner les sous-requêtes
Récapitulons tout et créons une requête qui affiche le nom de l’étudiant, la moyenne, le nombre de cours et le score total :
SELECT
s.id,
s.name,
(SELECT AVG(g.grade)
FROM grades g
WHERE g.student_id = s.id) AS average_grade,
(SELECT COUNT(*)
FROM enrollments e
WHERE e.student_id = s.id) AS course_count,
(SELECT SUM(g.grade)
FROM grades g
WHERE g.student_id = s.id) AS total_grade
FROM students s;
Cette requête retourne le profil complet de l’étudiant, construit grâce à la puissance des sous-requêtes. On voit la moyenne, le total des notes et le nombre de cours suivis par chaque étudiant. Ce genre de construction est un super moyen d’obtenir rapidement des infos agrégées sans créer de VIEW séparée ou faire des JOIN compliqués.
| id | name | average_grade | course_count | total_grade |
|---|---|---|---|---|
| 1 | Alex Lin | 87.5 | 2 | 175 |
| 2 | Anna Song | 76.0 | 1 | 76 |
| 3 | Dan Seth | 90.0 | 0 | 180 |
GO TO FULL VERSION