SQL Quest
Se connecter English 0 XP Niv. 1
Avancé

SQL : les sous-requêtes (IN, EXISTS, corrélées)

5 min de lecture · 4 exemples exécutables · mis à jour le 9 octobre 2026

Une sous-requête est une requête écrite à l'intérieur d'une autre, entre parenthèses. Elle permet de répondre en une seule fois à des questions en deux étapes, comme « quels produits coûtent plus cher que la moyenne ? » : il faut d'abord calculer la moyenne, puis comparer chaque produit à cette valeur.

Syntaxe

SELECT colonnes FROM table
WHERE colonne > (SELECT AVG(colonne) FROM table);

WHERE colonne IN (SELECT colonne FROM autre_table)
WHERE EXISTS (SELECT 1 FROM autre_table WHERE …)

Comment ça marche

Les trois formes les plus courantes

  • Une valeur : la sous-requête renvoie une seule valeur, que l'on compare avec =, >, <…
  • Une liste : la sous-requête renvoie une colonne, utilisée avec IN ou NOT IN.
  • Un test d'existence : EXISTS (…) est vrai si la sous-requête renvoie au moins une ligne.

Les sous-requêtes corrélées

Une sous-requête corrélée fait référence à la ligne en cours de la requête principale. Elle est donc recalculée pour chaque ligne. Exemple : comparer le salaire de chaque employé à la moyenne de son propre département.

Dans le FROM

Une sous-requête peut aussi servir de table temporaire dans le FROM (on lui donne alors un alias). Pour les requêtes complexes, une CTE (WITH) est souvent plus lisible.

Schéma

produits
nomprix
Clavier49
Plaid25
Gourde12
Casque34
résultat (moyenne = 30)
nomprix
Clavier49
Casque34
WHERE prix > (SELECT AVG(prix) …) : la sous-requête calcule d'abord la moyenne (30), puis chaque produit y est comparé.

Schéma simplifié pour illustrer le principe. Les exemples ci-dessous utilisent la vraie base d'exercices de SQL Quest (une boutique en ligne fictive : voir le schéma complet).

Exemples avec leurs résultats

1. Comparer à une valeur calculée

Les produits plus chers que le prix moyen.

SELECT nom, prix
FROM produits
WHERE prix > (SELECT AVG(prix) FROM produits)
ORDER BY prix DESC;
Essayer dans le bac à sable
Résultat
nomprix
Écran 27"342
SSD 1To317
Souris sans fil103
Webcam HD101
Casque gaming98
Bougie parfumée60,92

6 lignes au total

2. IN avec une sous-requête

Les clients qui ont au moins une commande annulée.

SELECT prenom, nom, ville
FROM clients
WHERE id IN (SELECT client_id FROM commandes WHERE statut = 'annulée')
LIMIT 8;
Essayer dans le bac à sable
Résultat
prenomnomville
SamirDuboisLille
SamirMartinLille
ZoéLaurentParis
AdamDuboisCasablanca
AliceFaureLyon
LéaBlancToulouse
JadeGarciaParis
CamilleLaurentBordeaux

8 lignes au total

3. Sous-requête corrélée

Les employés qui gagnent plus que la moyenne de leur département.

SELECT e.prenom, e.nom, e.departement, e.salaire
FROM employes e
WHERE e.salaire > (SELECT AVG(x.salaire) FROM employes x WHERE x.departement = e.departement);
Essayer dans le bac à sable
Résultat
prenomnomdepartementsalaire
JadeLefebvreVentes6 200
NathanRouxIT6 800
SamirSimonRH4 800
AliceSimonVentes4 800
LucasMoreauVentes4 600
InèsMercierVentes5 500
ChloéBenaliRH4 900
LouisChevalierRH5 300
LouisGarciaVentes5 500
LinaSimonVentes5 200

12 lignes au total · 10 affichées

4. NOT EXISTS

Les clients qui n'ont jamais commandé, écrits avec NOT EXISTS.

SELECT c.prenom, c.nom
FROM clients c
WHERE NOT EXISTS (SELECT 1 FROM commandes o WHERE o.client_id = c.id);
Essayer dans le bac à sable
Résultat
prenomnom
ManonRoux
AliceBernard
EmmaMoreau
NathanLefebvre
SarahSimon
KarimLefebvre
ThéoChevalier
ChloéLambert
LucasBenali
InèsLambert

10 lignes au total

Les erreurs fréquentes

Utiliser = avec une sous-requête qui renvoie plusieurs lignes

= attend une seule valeur. SQLite prend silencieusement la première, d'autres bases renvoient une erreur. Utilisez IN.

À éviter
SELECT * FROM clients WHERE id = (SELECT client_id FROM commandes);
Correct
SELECT * FROM clients WHERE id IN (SELECT client_id FROM commandes);

NOT IN avec des NULL

Si la sous-requête renvoie un seul NULL, NOT IN ne renvoie plus aucune ligne. NOT EXISTS n'a pas ce problème.

À éviter
SELECT * FROM employes WHERE id NOT IN (SELECT manager_id FROM employes);
Correct
SELECT * FROM employes e WHERE NOT EXISTS (SELECT 1 FROM employes x WHERE x.manager_id = e.id);

Oublier l'alias d'une sous-requête dans FROM

Une sous-requête utilisée comme table doit recevoir un nom pour pouvoir être utilisée ensuite.

À éviter
SELECT AVG(nb) FROM (SELECT COUNT(*) AS nb FROM commandes GROUP BY client_id);
Correct
SELECT AVG(nb) FROM (SELECT COUNT(*) AS nb FROM commandes GROUP BY client_id) AS t;

Exercices

Essayez d'écrire la requête vous-même avant d'ouvrir la solution.

Défi 1. Affichez le ou les produits les plus chers de chaque catégorie (sous-requête corrélée).

Voir une solution
SELECT categorie, nom, prix FROM produits p WHERE prix = (SELECT MAX(prix) FROM produits WHERE categorie = p.categorie);
categorienomprix
InformatiqueÉcran 27"342
MaisonBougie parfumée60,92
SportRaquette46,18
LivresManga tome 128,92
AlimentationHuile d'olive40,06

5 lignes au total

Essayer dans le bac à sable

Défi 2. Affichez les employés qui ne managent personne.

Voir une solution
SELECT prenom, nom FROM employes e WHERE NOT EXISTS (SELECT 1 FROM employes x WHERE x.manager_id = e.id);
prenomnom
AliceSimon
NathanMartin
LucasMoreau
EmmaLambert
AliceLefebvre
InèsMercier
HugoBonnet
ChloéBenali

21 lignes au total · 8 affichées

Essayer dans le bac à sable
Envie de vous entraîner davantage ?

Les exercices interactifs sont corrigés automatiquement, avec un prof qui explique vos erreurs. Une leçon résumée reste affichée à côté de l'éditeur.

Faire les exercices interactifs

Questions fréquentes

Sous-requête ou jointure : laquelle choisir ?

Les deux peuvent souvent donner le même résultat. Une sous-requête est plus lisible pour « filtrer selon une autre table », une jointure est nécessaire pour afficher des colonnes des deux tables.

Quelle différence entre IN et EXISTS ?

IN compare une valeur à une liste, EXISTS teste simplement si une ligne existe. NOT EXISTS est plus sûr que NOT IN quand la sous-requête peut contenir des NULL.

Une sous-requête corrélée est-elle lente ?

Elle est conceptuellement exécutée une fois par ligne, ce qui peut coûter cher sur de grosses tables. Les bases modernes l'optimisent souvent, mais une jointure ou une fonction de fenêtre est parfois plus rapide.

À lire ensuite