> Sous-requêtes SQL dans WHERE, FROM et SELECT, corrélées ou non, opérateurs IN, EXISTS, ANY, ALL, et comparaison avec les jointures : le guide complet avec exemples.

*Source : https://coder-studio.com/tutoriels/sous-requetes-sql/*

---

[// Bases de données](https://coder-studio.com/tutoriels/)

# Les sous-requêtes SQL expliquées (subqueries)

Par **Kévin Papot** · publié le 9 décembre 2025

Une **sous-requête** (ou _subquery_) est une requête `SELECT` imbriquée à l’intérieur d’une autre requête SQL. Elle permet de calculer une valeur intermédiaire, de filtrer sur un ensemble dynamique ou de construire une table temporaire, le tout en une seule instruction. Bien maîtrisées, les sous-requêtes rendent lisibles des interrogations qui seraient sinon très lourdes. Ce guide en fait le tour, avec des exemples concrets et les cas où une jointure serait préférable.

Pour tous les exemples, nous utiliserons deux tables classiques : des employés et leurs services.

```
CREATE TABLE services (
    id  INTEGER PRIMARY KEY,
    nom TEXT
);

CREATE TABLE employes (
    id         INTEGER PRIMARY KEY,
    nom        TEXT,
    salaire    NUMERIC,
    service_id INTEGER
);

INSERT INTO services VALUES
    (1, 'Ingénierie'), (2, 'Ventes'), (3, 'RH');

INSERT INTO employes VALUES
    (1, 'Alice', 4500, 1),
    (2, 'Bob',   3800, 1),
    (3, 'Chloé', 4200, 2),
    (4, 'David', 3000, 2),
    (5, 'Emma',  3500, NULL);   -- pas encore affectée
```

## Où placer une sous-requête ?

Une sous-requête peut apparaître à trois endroits principaux : dans le `WHERE` (pour filtrer), dans le `FROM` (comme table dérivée) et dans le `SELECT` (comme colonne calculée). Passons-les en revue.

### Sous-requête dans le WHERE

C’est l’usage le plus courant. La sous-requête calcule une valeur ou un ensemble, que la requête externe utilise pour filtrer. Cherchons les employés dont le salaire dépasse la moyenne générale :

```
SELECT nom, salaire
FROM employes
WHERE salaire > (SELECT AVG(salaire) FROM employes);
```

La sous-requête `(SELECT AVG(salaire) FROM employes)` renvoie une seule valeur (la moyenne), comparée ensuite ligne par ligne. On parle de **sous-requête scalaire** quand elle retourne une valeur unique.

### Sous-requête dans le FROM

Placée dans le `FROM`, la sous-requête agit comme une **table dérivée** : un résultat intermédiaire que l’on interroge à son tour. Elle doit obligatoirement recevoir un alias.

```
SELECT service_id, salaire_moyen
FROM (
    SELECT service_id, AVG(salaire) AS salaire_moyen
    FROM employes
    GROUP BY service_id
) AS moyennes
WHERE salaire_moyen > 3900;
```

On calcule d’abord le salaire moyen par service, puis on filtre ces moyennes. Ce découpage en deux étapes est souvent plus clair qu’une requête unique surchargée.

### Sous-requête dans le SELECT

Une sous-requête scalaire peut aussi fournir une colonne du résultat, évaluée pour chaque ligne. Affichons chaque employé avec le nom de son service :

```
SELECT
    e.nom,
    e.salaire,
    (SELECT s.nom FROM services s WHERE s.id = e.service_id) AS service
FROM employes e;
```

Ici, la sous-requête est **corrélée** : elle dépend de `e.service_id`, la valeur de la ligne courante. Nous y revenons juste après. Ce résultat pourrait aussi s’obtenir par une jointure, souvent plus efficace.

## Sous-requêtes corrélées ou non ?

La distinction est fondamentale.

Une **sous-requête non corrélée** est indépendante : elle pourrait s’exécuter seule, et son résultat ne dépend pas de la requête externe. Le moteur ne l’évalue qu’une fois. C’était le cas de la moyenne des salaires plus haut.

Une **sous-requête corrélée** référence une colonne de la requête externe. Elle est donc réévaluée pour chaque ligne examinée. Exemple : les employés qui gagnent plus que la moyenne **de leur propre service**.

```
SELECT nom, salaire, service_id
FROM employes e
WHERE salaire > (
    SELECT AVG(salaire)
    FROM employes
    WHERE service_id = e.service_id   -- corrélation
);
```

Pour chaque employé `e`, la sous-requête recalcule la moyenne de son service. C’est puissant, mais potentiellement coûteux sur de gros volumes, car l’opération se répète à chaque ligne.

## Les opérateurs de sous-requête

Quand une sous-requête renvoie plusieurs lignes, on ne peut plus utiliser un simple `=`. On recourt à des opérateurs dédiés.

### IN

`IN` teste l’appartenance à l’ensemble renvoyé. Trouvons les employés des services situés « Ingénierie » ou « Ventes » :

```
SELECT nom
FROM employes
WHERE service_id IN (
    SELECT id FROM services
    WHERE nom IN ('Ingénierie', 'Ventes')
);
```

Attention avec `NOT IN` : si la sous-requête renvoie une valeur `NULL`, le résultat de `NOT IN` peut devenir vide de façon contre-intuitive. Dans ce cas, `NOT EXISTS` est plus sûr.

### EXISTS

`EXISTS` renvoie vrai dès que la sous-requête produit au moins une ligne. On l’emploie surtout avec une corrélation, pour tester une existence. Listons les services qui ont au moins un employé :

```
SELECT s.nom
FROM services s
WHERE EXISTS (
    SELECT 1
    FROM employes e
    WHERE e.service_id = s.id
);
```

Le `SELECT 1` est une convention : le contenu importe peu, seule compte l’existence d’une ligne. `EXISTS` s’arrête dès la première correspondance trouvée, ce qui le rend souvent efficace.

### ANY et ALL

`ANY` (synonyme `SOME`) et `ALL` comparent une valeur à **tout** l’ensemble renvoyé. `= ANY` équivaut à `IN`. Ils prennent tout leur sens avec `>`, `<`, etc.

```
-- Employés payés plus que AU MOINS un employé du service Ventes
SELECT nom, salaire
FROM employes
WHERE salaire > ANY (
    SELECT salaire FROM employes WHERE service_id = 2
);

-- Employés payés plus que TOUS les employés du service Ventes
SELECT nom, salaire
FROM employes
WHERE salaire > ALL (
    SELECT salaire FROM employes WHERE service_id = 2
);
```

`> ANY` signifie « supérieur au plus petit », `> ALL` signifie « supérieur au plus grand ». Ces opérateurs, moins connus, remplacent élégamment des sous-requêtes avec `MIN` ou `MAX`.

## Sous-requête ou jointure ?

Beaucoup de sous-requêtes peuvent se réécrire en jointures, et inversement. Le choix relève souvent de la lisibilité et des performances. Comparons deux façons de lister les employés avec le nom de leur service.

```
-- Version sous-requête corrélée
SELECT
    e.nom,
    (SELECT s.nom FROM services s WHERE s.id = e.service_id) AS service
FROM employes e;

-- Version jointure (souvent plus performante)
SELECT e.nom, s.nom AS service
FROM employes e
LEFT JOIN services s ON s.id = e.service_id;
```

En règle générale, une **jointure** est préférable quand on veut ramener des colonnes de plusieurs tables, car le moteur l’optimise bien. Une **sous-requête** brille pour les tests d’existence (`EXISTS`), les filtres sur un agrégat (comparer à une moyenne), ou quand elle rend l’intention plus explicite. Sur MySQL notamment, une sous-requête corrélée dans le `SELECT` peut être plus lente qu’une jointure équivalente. Si le sujet des jointures vous manque, notre [guide des jointures SQL](https://coder-studio.com/tutoriels/jointures-sql/) le détaille en profondeur.

## En résumé

Les sous-requêtes imbriquent un `SELECT` dans un autre pour filtrer (`WHERE`), créer une table temporaire (`FROM`) ou calculer une colonne (`SELECT`). Distinguez bien les versions **non corrélées** (évaluées une fois) des **corrélées** (réévaluées par ligne, plus coûteuses). Maîtrisez les opérateurs `IN`, `EXISTS`, `ANY` et `ALL`, en gardant `EXISTS`/`NOT EXISTS` comme réflexe sûr face aux `NULL`. Enfin, gardez en tête qu’une jointure est souvent plus efficace pour ramener des colonnes : sous-requêtes et jointures sont deux outils complémentaires, pas concurrents.

## À lire ensuite

-   [Les jointures SQL expliquées (INNER, LEFT, RIGHT…)](https://coder-studio.com/tutoriels/jointures-sql/)
-   [Les index SQL : accélérer ses requêtes](https://coder-studio.com/tutoriels/index-sql-performance/)
-   [MySQL : le guide complet pour débuter](https://coder-studio.com/tutoriels/mysql-guide-debutant/)
