Les sous-requêtes SQL (subqueries) expliquées avec exemples
Une sous-requête (ou subquery) est une requête SQL imbriquée à l’intérieur d’une autre. L’idée est simple et puissante : le résultat d’une première requête sert de donnée d’entrée à une seconde. Cela permet de répondre en une seule fois à des questions qui demanderaient sinon deux étapes — comme « quels employés gagnent plus que la moyenne ? », où il faut d’abord calculer la moyenne, puis comparer chaque salaire à celle-ci.
Cet article explique les principaux usages des sous-requêtes, avec des exemples concrets et leur résultat. Il suppose que vous êtes à l’aise avec les bases de SELECT et WHERE.
Le jeu de données d’exemple
Les exemples s’appuient sur une table employes :
| nom | service | salaire |
|---|---|---|
| Martin | Informatique | 3200 |
| Durand | Comptabilité | 2800 |
| Petit | Informatique | 3600 |
| Moreau | Comptabilité | 2900 |
| Leroy | Informatique | 4100 |
La moyenne des salaires est de 3320 — gardez ce chiffre en tête, il va servir.
Sous-requête qui renvoie une seule valeur
Le cas le plus courant : une sous-requête calcule une valeur unique, utilisée dans la condition de la requête principale. Reprenons « les employés gagnant plus que la moyenne » :
SELECT nom, salaire
FROM employes
WHERE salaire > (SELECT AVG(salaire) FROM employes);La sous-requête (SELECT AVG(salaire) FROM employes) est évaluée d’abord : elle renvoie 3320. La requête principale devient alors « WHERE salaire > 3320 ». Résultat :
| nom | salaire |
|---|---|
| Petit | 3600 |
| Leroy | 4100 |
L’intérêt est décisif : on ne pourrait pas écrire WHERE salaire > AVG(salaire) directement (une fonction d’agrégation n’est pas autorisée dans un WHERE). La sous-requête contourne élégamment cette limite.
Sous-requête qui renvoie une liste : IN
Quand la sous-requête renvoie plusieurs valeurs, on l’utilise avec IN plutôt qu’avec =. Par exemple, tous les employés travaillant dans un service où figure au moins un salaire supérieur à 4000 :
SELECT nom, service
FROM employes
WHERE service IN (
SELECT service FROM employes WHERE salaire > 4000
);La sous-requête renvoie la liste des services concernés (ici « Informatique », grâce à Leroy). La requête principale sélectionne alors tous les employés de ces services. Résultat : Martin, Petit et Leroy (les trois de l’Informatique).
Sous-requête corrélée
Dans une sous-requête corrélée, la sous-requête fait référence à la requête externe : elle est réévaluée pour chaque ligne. Par exemple, les employés qui gagnent plus que la moyenne de leur propre service :
SELECT nom, service, salaire
FROM employes e
WHERE salaire > (
SELECT AVG(salaire)
FROM employes
WHERE service = e.service
);Ici, la sous-requête utilise e.service, qui vient de la ligne en cours d’examen dans la requête externe. Pour chaque employé, la base recalcule la moyenne de son service et compare. C’est plus lourd à exécuter (une évaluation par ligne), mais très expressif.
EXISTS : tester l’existence
L’opérateur EXISTS renvoie vrai dès que la sous-requête ramène au moins une ligne. Il sert à tester une présence sans se soucier des valeurs. Par exemple, les services qui comptent au moins un employé (sur une table services) :
SELECT libelle
FROM services s
WHERE EXISTS (
SELECT 1 FROM employes e
WHERE e.id_service = s.id_service
);La convention SELECT 1 est courante avec EXISTS : peu importe ce que la sous-requête sélectionne, seule compte l’existence d’une ligne. NOT EXISTS fait l’inverse (les services sans aucun employé).
Sous-requête dans le FROM
Une sous-requête peut aussi remplacer une table dans le FROM : on parle alors de table dérivée. Utile pour agréger puis filtrer sur le résultat. Par exemple, afficher les services dont l’effectif dépasse 2 :
SELECT service, effectif
FROM (
SELECT service, COUNT(*) AS effectif
FROM employes
GROUP BY service
) AS stats
WHERE effectif > 2;La sous-requête interne calcule l’effectif par service (voir notre article sur GROUP BY), et la requête externe filtre ce résultat. Une table dérivée doit toujours recevoir un alias (ici stats).
Sous-requête ou jointure ?
Beaucoup de sous-requêtes avec IN ou EXISTS peuvent se réécrire avec une jointure, souvent plus performante sur de gros volumes. Le choix dépend du cas :
- Sous-requête : plus lisible quand la logique est « filtrer selon un calcul » (moyenne, existence…).
- Jointure : préférable quand on a besoin de colonnes des deux tables dans le résultat, et généralement plus rapide.
Il n’y a pas de règle absolue : privilégiez d’abord la lisibilité, puis optimisez si les performances l’exigent.
En résumé
Une sous-requête imbrique une requête dans une autre : elle peut renvoyer une valeur unique (avec =, >…), une liste (avec IN), tester une existence (EXISTS), ou servir de table dérivée dans le FROM. Les sous-requêtes corrélées, réévaluées ligne par ligne, ouvrent des possibilités puissantes. Retenez le réflexe : dès qu’une condition dépend d’un calcul sur les données elles-mêmes, la sous-requête est souvent la réponse la plus claire.