Félicitations, on arrive au moment où ça devient vraiment fun ! Aujourd’hui, on va voir comment mélanger différents types de sous-requêtes pour résoudre des problèmes bien velus. EXISTS, IN, HAVING — ce trio magique va te faire sentir comme un vrai magicien des bases de données. On va extraire des données d’une table, les filtrer avec les infos d’une autre, grouper, puis filtrer les groupes. Et en bonus, on va voir quelques astuces pour rendre tes requêtes plus efficaces.
Allez, on commence par poser un problème général qu’on va résoudre petit à petit pendant la leçon.
Énoncé du problème
Imaginons qu’on a une base de données d’université avec trois tables :
Table students
| id | name | group_id |
|---|---|---|
| 1 | Otto | 101 |
| 2 | Maria | 101 |
| 3 | Alex | 102 |
| 4 | Anna | 103 |
Table courses
| id | name |
|---|---|
| 1 | Mathématiques |
| 2 | Programmation |
| 3 | Philosophie |
Table enrollments
| student_id | course_id | grade |
|---|---|---|
| 1 | 1 | 90 |
| 1 | 2 | NULL |
| 2 | 1 | 85 |
| 3 | 3 | 70 |
On doit sélectionner tous les étudiants qui :
- Sont inscrits à au moins un cours
EXISTS. - N’ont pas de note pour au moins un des cours où ils sont inscrits
IN. - Appartiennent à des groupes où la moyenne est supérieure à 80
HAVING.
Solution avec EXISTS et IN
Étape 1 : Vérifier les étudiants inscrits (EXISTS). On commence par la condition la plus simple. On veut savoir quels étudiants sont inscrits à au moins un cours. Pour ça, on peut utiliser EXISTS.
SELECT name
FROM students s
WHERE EXISTS (
SELECT 1
FROM enrollments e
WHERE e.student_id = s.id
);
- La requête principale sélectionne les noms depuis la table
students. - Dans la sous-requête, on vérifie s’il y a des lignes dans
enrollmentsqui correspondent à l’étudiant du SELECT principal (WHERE e.student_id = s.id). SELECT 1sert juste à dire qu’on veut savoir si une ligne existe, pas son contenu.
Résultat :
| name |
|---|
| Otto |
| Maria |
| Alex |
Maintenant, on sait qui est inscrit à des cours. Mais on veut aller plus loin. On veut filtrer ceux qui n’ont pas de note.
Étape 2 : Vérifier l’absence de note (IN + NULL). Maintenant, on ajoute un filtre : on veut seulement les étudiants qui n’ont pas de note pour au moins un cours. Ici, IN et la gestion de NULL vont nous aider.
SELECT name
FROM students s
WHERE id IN (
SELECT e.student_id
FROM enrollments e
WHERE e.grade IS NULL
);
- Dans la requête principale, on sélectionne les noms des étudiants.
- La sous-requête construit une liste de
student_iddepuisenrollmentsoùgrade IS NULL.
Résultat :
| name |
|---|
| Otto |
Donc, Otto est le seul étudiant qui a un cours sans note. La tension monte ! Mais ce n’est pas fini : il faut prendre en compte seulement les groupes où la moyenne est supérieure à 80.
Solution avec HAVING
Étape 3 : Grouper et filtrer avec HAVING.
Là, il est temps de tout combiner. On doit :
- Calculer la moyenne pour chaque groupe.
- Filtrer les groupes où la moyenne est supérieure à 80.
- Afficher les étudiants de ces groupes, en tenant compte des conditions précédentes.
SELECT name
FROM students s
WHERE s.group_id IN (
SELECT group_id
FROM students
JOIN enrollments ON students.id = enrollments.student_id
WHERE grade IS NOT NULL
GROUP BY group_id
HAVING AVG(grade) > 80
)
AND id IN (
SELECT e.student_id
FROM enrollments e
WHERE e.grade IS NULL
);
- La requête principale sélectionne les noms des étudiants qui remplissent toutes les conditions.
- La première sous-requête dans le
WHEREretourne la liste desgroup_idpour les groupes avec une moyenne supérieure à 80.- On fait un JOIN entre
studentsetenrollmentspour récupérer les notes. - On ne garde que les lignes où
grade IS NOT NULL. - On groupe par
group_id. - On utilise
HAVINGpour filtrer les groupes.
- On fait un JOIN entre
- La deuxième sous-requête dans le
WHEREvérifie que l’étudiant a au moins un cours sans note. - Les deux conditions sont combinées avec
AND.
Résultat :
| name |
|---|
| Otto |
Donc, on a vu qu’Otto est non seulement le seul étudiant sans note, mais il est aussi dans un groupe qui cartonne.
Comparer les approches : EXISTS vs IN
EXISTS marche super bien quand tu veux juste vérifier si des lignes existent. C’est efficace, car il s’arrête dès qu’il trouve la première. C’est top pour les grosses tables.
En même temps, IN est utile quand tu veux bosser sur le contenu. Par exemple, si tu veux sortir une liste d’identifiants (id) pour filtrer ensuite. Mais attention, IN peut devenir lent si la sous-requête retourne beaucoup de valeurs.
Quand utiliser HAVING
Pour les données agrégées, quand tu dois filtrer sur le résultat d’un calcul, HAVING est le meilleur choix. Mais si tu peux déplacer la condition dans le WHERE (genre filtrer sur une colonne), ça simplifie la requête et ça va plus vite.
Exemple complet
Pour bien ancrer tout ça, on va voir un autre exemple : sélectionner les groupes où au moins un étudiant a une note inférieure à 75, mais qui ne sont pas inscrits au cours "Philosophie".
Petit rappel de nos tables :
Table students
| id | name | group_id |
|---|---|---|
| 1 | Otto | 101 |
| 2 | Maria | 101 |
| 3 | Alex | 102 |
| 4 | Anna | 103 |
Table courses
| id | name |
|---|---|
| 1 | Mathématiques |
| 2 | Programmation |
| 3 | Philosophie |
Table enrollments
| student_id | course_id | grade |
|---|---|---|
| 1 | 1 | 90 |
| 1 | 2 | NULL |
| 2 | 1 | 85 |
| 3 | 3 | 70 |
SELECT DISTINCT group_id
FROM students s
WHERE group_id IN (
SELECT s.group_id
FROM students s
JOIN enrollments e ON s.id = e.student_id
WHERE e.grade < 75
)
AND group_id NOT IN (
SELECT s.group_id -- sous-requête de niveau 1
FROM students s
JOIN enrollments e ON s.id = e.student_id
WHERE e.course_id = (
SELECT id FROM courses WHERE name = 'Philosophie' -- sous-requête de niveau 2 :P
)
);
- La première sous-requête sélectionne les groupes où il y a au moins un étudiant avec une note inférieure à 75.
- La deuxième sous-requête exclut les groupes liés au cours "Philosophie".
- On combine les conditions avec
INetNOT INpour obtenir le résultat final.
Résultat :
| group_id |
|---|
| 101 |
À quel point c’est utile ?
Dans la vraie vie, ces techniques te sauvent quand tu dois analyser des relations complexes entre les données. Par exemple :
- En analytics pour repérer des groupes "spéciaux" de clients (VIP, à problèmes, etc.).
- En développement de systèmes de recommandation, quand tu filtres les utilisateurs sur plein de critères.
- En entretien, quand on te demande d’optimiser une requête SQL bien costaud.
Entraîne-toi ! C’est comme ça qu’on devient un boss.
GO TO FULL VERSION