%% nav-debut : généré par gen_navigation.py, ne pas modifier à la main %%
> [!nav]
> ↩ [[Code et Développement/Développement d'Applications/SQL IV/SQL IV|Accueil · SQL IV]]
>
> | [[Code et Développement/Développement d'Applications/SQL IV/04_La jointure complète - FULL JOIN\|← La jointure complète : FULL JOIN]] | [[Code et Développement/Développement d'Applications/SQL IV/06_Sous-requête renvoyant une valeur\|Sous-requête renvoyant une valeur →]] |
> |:---|---:|
%% nav-fin %%
[[Code et Développement/Développement d'Applications/SQL IV/04_La jointure complète - FULL JOIN|La jointure complète - FULL JOIN]]
# Les sous-requêtes : vue d'ensemble
## Introduction
Dans le chapitre [[Code et Développement/Développement d'Applications/SQL I/05_Comment répondre à une demande|Comment répondre à une demande]] de SQL I, nous avons vu qu'une demande peut nécessiter **plusieurs requêtes** : le résultat d'une requête sert d'entrée à une autre. Dans SQL III, vous avez placé une requête dans le `FROM` d'une autre requête.
Une requête placée à l'intérieur d'une autre s'appelle une **sous-requête**. Dans SQL IV, nous allons voir qu'elle peut se placer **ailleurs que dans le `FROM`**.
### Objectifs du chapitre
À la fin de ce chapitre, vous serez capable de :
- Reconnaître une sous-requête et savoir dans quel ordre elle est exécutée.
- Distinguer les trois formes de la table renvoyée par une sous-requête : une table classique, une table d'une seule valeur, une table d'un seul champ.
- Savoir où chaque forme peut être placée.
---
## 1. Un besoin : comparer à un résultat calculé
On nous demande : ***quelles sont les commandes dont le montant est supérieur au montant moyen des commandes ?***
Nous utilisons la table *Commandes* présentée au chapitre [[Code et Développement/Développement d'Applications/SQL IV/01_Supprimer les doublons - DISTINCT|Supprimer les doublons : DISTINCT]].
Une idée naturelle serait d'écrire :
```sql
SELECT id_commande, montant_total
FROM Commandes
WHERE montant_total > AVG(montant_total); -- ERREUR
```
Cette requête provoque une erreur. Pourquoi ? Le `WHERE` examine les enregistrements **un par un**, alors qu'une moyenne se calcule sur **un ensemble** d'enregistrements. Une fonction d'agrégat ne peut donc pas être utilisée dans un `WHERE` (voir le chapitre [[Code et Développement/Développement d'Applications/SQL I/03_ Opérations entre les enregistrements|Opérations entre les enregistrements]]).
Appliquons la méthode de SQL I : le montant moyen n'existe pas dans la base, **il faut une requête pour le calculer**.
```sql
SELECT AVG(montant_total)
FROM Commandes;
```
| AVG(montant_total) |
| ------------------ |
| 267.142857 |
> [!example] Ce que vous savez déjà faire : la sous-requête dans le `FROM`
> Avec la méthode de SQL I (appliquée en SQL III), on place cette requête dans le `FROM`, comme une table à part :
>
> ```sql
> SELECT Co.id_commande, Co.montant_total
> FROM Commandes Co,
> (SELECT AVG(montant_total) AS montant_moyen FROM Commandes) AS m
> WHERE Co.montant_total > m.montant_moyen;
> ```
>
> | id_commande | montant_total |
> | ----------- | ------------- |
> | 103 | 560.00 |
> | 105 | 310.00 |
> | 107 | 400.00 |
>
> La sous-requête `m` ne contient **qu'une seule ligne** (la moyenne). La virgule réalise un produit cartésien entre les commandes et cette ligne : cela revient à ajouter le champ `montant_moyen` à chaque commande. Le `WHERE` peut alors comparer les deux champs.
>
> Cette écriture est juste, et vous pouvez continuer à l'utiliser.
Dans SQL IV, nous découvrons une écriture plus directe : placer la requête, **entre parenthèses**, là où on a besoin de sa valeur :
```sql
SELECT id_commande, montant_total
FROM Commandes
WHERE montant_total > (SELECT AVG(montant_total) FROM Commandes);
```
| id_commande | montant_total |
| ----------- | ------------- |
| 103 | 560.00 |
| 105 | 310.00 |
| 107 | 400.00 |
> [!definition] Définition : sous-requête
> Une **sous-requête** est une requête `SELECT` écrite **entre parenthèses** à l'intérieur d'une autre requête, appelée **requête principale**.
> [!note] Les deux écritures font le même travail
> - Elles donnent le même résultat.
> - Dans les deux cas, la moyenne n'est calculée **qu'une seule fois** : la sous-requête n'utilise aucun champ de la requête principale, son résultat est donc le même pour toutes les commandes. Le logiciel le calcule une fois et le réutilise pour chaque commande.
> - La version dans le `WHERE` se lit comme la demande : « les commandes dont le montant est supérieur à (la moyenne) ».
---
## 2. Dans quel ordre est-ce exécuté ?
La sous-requête est exécutée **en premier**. Son résultat remplace la sous-requête dans la requête principale, qui est exécutée ensuite.
```mermaid
graph TD
A@{ shape: text, label: "Commandes" }-->S["Sous-requête<br/>SELECT AVG(montant_total) FROM Commandes"]
S-->V@{ shape: text, label: "267.14" }
B@{ shape: text, label: "Commandes" }-->P["Requête principale"]
V-->P
P-->R@{ shape: text, label: "Commandes de plus de 267.14 €" }
```
Pour la requête principale, tout se passe comme si on avait écrit :
```sql
SELECT id_commande, montant_total
FROM Commandes
WHERE montant_total > 267.142857;
```
> [!tip] Lire une requête avec sous-requête
> On lit **de l'intérieur vers l'extérieur** :
> 1. Je cherche la sous-requête (entre parenthèses) et je comprends ce qu'elle renvoie.
> 2. Je remplace mentalement la sous-requête par son résultat.
> 3. Je lis la requête principale comme une requête habituelle.
> [!note] Une exception
> Il existe des sous-requêtes qui utilisent un champ de la requête principale : elles sont exécutées **une fois pour chaque enregistrement** de la requête principale. Nous les verrons au chapitre [[Code et Développement/Développement d'Applications/SQL IV/08_Tester l'existence - EXISTS|Tester l'existence : EXISTS]].
---
## 3. Trois formes de table
> [!important] Rappel : le résultat d'une requête est toujours une table
> Comme nous l'avons vu dans le chapitre [[Code et Développement/Développement d'Applications/SQL I/01_ Détails sur les 6 mots clés d'une requête sélection|Détails sur les 6 mots clés d'une requête sélection]], le résultat d'une requête `SELECT` est toujours une **table éphémère**. C'est vrai aussi pour une sous-requête.
Ce qui change d'une sous-requête à l'autre, c'est **la forme** de cette table. Pour savoir **où** placer une sous-requête, il faut connaître cette forme. Il y a trois cas :
| Forme de la table | Champs | Enregistrements | Exemple | Raccourci utilisé dans le cours |
| --- | --- | --- | --- | --- |
| **Table classique** | un ou plusieurs | un ou plusieurs | le chiffre d'affaires de chaque client | « une table » |
| **Table d'une seule valeur** | un seul | un seul | le montant moyen des commandes | « une valeur » |
| **Table d'un seul champ** | un seul | plusieurs | les identifiants des clients qui ont commandé | « une liste » |
Exemples :
**Table classique** : `SELECT id_client, SUM(montant_total) AS ca FROM Commandes GROUP BY id_client`
| id_client | ca |
| --------- | ------ |
| 1 | 370.00 |
| 2 | 950.00 |
| 3 | 150.00 |
| 4 | 400.00 |
**Table d'une seule valeur** : `SELECT AVG(montant_total) FROM Commandes`
| AVG(montant_total) |
| ------------------ |
| 267.142857 |
**Table d'un seul champ** : `SELECT id_client FROM Commandes`
| id_client |
| --------- |
| 1 |
| 1 |
| 2 |
| 2 |
| 2 |
| 3 |
| 4 |
> [!note] Pourquoi ces raccourcis ?
> Une table d'une seule valeur peut être utilisée **comme une valeur** (un nombre, une date…) : on peut la comparer avec `>` ou `=`. Une table d'un seul champ peut être utilisée **comme une liste de valeurs**, celle que l'on écrivait à la main après `IN` en SQL I. Dans la suite du cours, « la sous-requête renvoie une valeur » signifie donc « la sous-requête renvoie une table d'une seule valeur », et « une liste » signifie « une table d'un seul champ ».
> [!tip] Une table d'une seule valeur reste une table
> Elle peut donc aussi être placée dans le `FROM` : c'est ce que vous faites depuis SQL I (chapitre [[Code et Développement/Développement d'Applications/SQL I/05_Comment répondre à une demande|Comment répondre à une demande]]) et SQL III, et c'est l'encadré « Ce que vous savez déjà faire » du point 1. Dans SQL IV, vous découvrez qu'elle peut **aussi** se placer dans le `WHERE`, le `HAVING` ou le `SELECT`. Les deux possibilités sont justes.
---
## 4. Où placer une sous-requête ?
| La sous-requête renvoie une table… | Je peux la placer… | Avec… | Chapitre |
| --- | --- | --- | --- |
| **classique** | dans le `FROM` | un alias obligatoire : `(SELECT …) AS t` | déjà vu en SQL III |
| **d'une seule valeur** | dans le `FROM`, séparée des autres tables par une virgule | un alias obligatoire : `FROM Commandes Co, (SELECT AVG(…) …) AS m` ; la valeur est ajoutée à chaque enregistrement (produit cartésien avec une table d'une seule ligne) | déjà vu en SQL I et SQL III, et dans le point 1 ci-dessus |
| **d'une seule valeur** | dans le `WHERE` ou le `HAVING` | `=`, `<>`, `<`, `>`, `<=`, `>=` | [[Code et Développement/Développement d'Applications/SQL IV/06_Sous-requête renvoyant une valeur\|Sous-requête renvoyant une valeur]] |
| **d'une seule valeur** | dans le `SELECT` | un alias pour nommer la colonne | [[Code et Développement/Développement d'Applications/SQL IV/06_Sous-requête renvoyant une valeur\|Sous-requête renvoyant une valeur]] |
| **d'un seul champ** | dans le `WHERE` ou le `HAVING` | `IN` ou `NOT IN` | [[Code et Développement/Développement d'Applications/SQL IV/07_Sous-requête renvoyant une liste - IN\|Sous-requête renvoyant une liste : IN]] |
| **de n'importe quelle forme** (on teste seulement si elle contient au moins un enregistrement) | dans le `WHERE` | `EXISTS` ou `NOT EXISTS` | [[Code et Développement/Développement d'Applications/SQL IV/08_Tester l'existence - EXISTS\|Tester l'existence : EXISTS]] |
> [!warning] La forme doit correspondre à l'endroit
> - Avec `>` ou `=`, la sous-requête doit renvoyer une table d'**une seule valeur** (un seul champ, un seul enregistrement). On ne peut pas comparer un montant à une liste de montants.
> - Avec `IN`, la sous-requête doit renvoyer une table d'**un seul champ**.
> - Dans le `FROM`, la sous-requête doit avoir **un alias**.
---
## 5. Les bonnes habitudes
> [!tip] Écrire une sous-requête en 3 temps
> 1. **Écrivez et exécutez la sous-requête seule.** Vérifiez son résultat : est-ce une table classique, une table d'une seule valeur, une table d'un seul champ ? Les valeurs sont-elles correctes ?
> 2. **Écrivez la requête principale** en remplaçant la sous-requête par une valeur « en dur » (par exemple `267.14`) et vérifiez qu'elle fonctionne.
> 3. **Remplacez la valeur en dur par la sous-requête** entre parenthèses.
> [!tip] Rendre la requête lisible
> - Décalez la sous-requête vers la droite (indentation).
> - Ajoutez un commentaire avec `--` pour dire ce qu'elle calcule.
>
> ```sql
> SELECT id_commande, montant_total
> FROM Commandes
> WHERE montant_total > (
> -- montant moyen de toutes les commandes
> SELECT AVG(montant_total)
> FROM Commandes
> );
> ```
> [!note] Le lien avec la méthode de SQL I
> Dans le chapitre [[Code et Développement/Développement d'Applications/SQL I/05_Comment répondre à une demande|Comment répondre à une demande]], chaque rectangle du diagramme est une requête. Avec les sous-requêtes, chaque requête « intermédiaire » devient une sous-requête placée **là où son résultat est utilisé**. La fiche de préparation reste la même : on la remplit pour chaque requête, en commençant par les plus « profondes ».
---
> [!important] Ce qu'il faut retenir
> - Une sous-requête est une requête `SELECT` entre parenthèses, à l'intérieur d'une autre requête.
> - Elle est exécutée en premier ; on lit donc de l'intérieur vers l'extérieur.
> - Elle renvoie toujours une table : une **table classique**, une table d'**une seule valeur** (« une valeur ») ou une table d'**un seul champ** (« une liste »). C'est cette forme qui détermine où on peut la placer.
> - On écrit et on teste toujours la sous-requête seule avant de l'insérer.
Prochaine étape, les sous-requêtes qui renvoient une seule valeur :
[[Code et Développement/Développement d'Applications/SQL IV/06_Sous-requête renvoyant une valeur|Sous-requête renvoyant une valeur]]
%% nav-debut : généré par gen_navigation.py, ne pas modifier à la main %%
> [!nav]
> ↩ [[Code et Développement/Développement d'Applications/SQL IV/SQL IV|Accueil · SQL IV]]
>
> | [[Code et Développement/Développement d'Applications/SQL IV/04_La jointure complète - FULL JOIN\|← La jointure complète : FULL JOIN]] | [[Code et Développement/Développement d'Applications/SQL IV/06_Sous-requête renvoyant une valeur\|Sous-requête renvoyant une valeur →]] |
> |:---|---:|
%% nav-fin %%
---
🗓️ Historique
Dernière mise à jour : `30 octobre 2026`
Rédigé par : [[Vincent TELLIER]]