Les Sous-RequĂȘtes SQL
Imbriquer des SELECT â dans WHERE, FROM, SELECT, avec IN, EXISTS, ANY et ALL
C’est quoi une sous-requĂȘte ?
Une sous-requĂȘte (ou subquery) est une requĂȘte SELECT imbriquĂ©e dans une autre requĂȘte. Elle est entourĂ©e de parenthĂšses et peut apparaĂźtre dans le WHERE, le FROM ou le SELECT.
SELECT AVG(montant) FROM commandes; — 246
— Sous-requĂȘte : les commandes au-dessus de la moyenne
SELECT * FROM commandes
WHERE montant > (SELECT AVG(montant) FROM commandes);
— ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
— sous-requĂȘte scalaire
La sous-requĂȘte est exĂ©cutĂ©e en premier, puis son rĂ©sultat est utilisĂ© par la requĂȘte principale. C’est comme une variable temporaire calculĂ©e Ă la volĂ©e.
Sous-requĂȘte dans le WHERE
SELECT produit, montant
FROM commandes
WHERE montant > (SELECT AVG(montant) FROM commandes);
— Le client qui a la commande la plus chĂšre
SELECT * FROM clients
WHERE id = (
SELECT client_id FROM commandes
ORDER BY montant DESC LIMIT 1
);
— Le dernier produit commandĂ©
SELECT * FROM commandes
WHERE date_commande = (SELECT MAX(date_commande) FROM commandes);
Une sous-requĂȘte scalaire doit retourner exactement une ligne et une colonne. Si elle retourne plusieurs lignes avec =, tu auras une erreur. Utilise IN pour plusieurs lignes.
Sous-requĂȘte avec IN / NOT IN
SELECT * FROM clients
WHERE id IN (SELECT client_id FROM commandes);
— Clients qui n’ont JAMAIS commandĂ©
SELECT * FROM clients
WHERE id NOT IN (SELECT client_id FROM commandes);
— Produits achetĂ©s par Alice
SELECT * FROM produits
WHERE id IN (
SELECT produit_id FROM commandes
WHERE client_id = (SELECT id FROM clients WHERE nom = ‘Alice’)
);
â ïž PiĂšge de NOT IN avec NULL : si la sous-requĂȘte retourne un NULL parmi les valeurs, NOT IN ne retourne aucun rĂ©sultat. Exemple : 5 NOT IN (1, 2, NULL) = UNKNOWN, pas TRUE. Utilise NOT EXISTS pour Ă©viter ce piĂšge.
Sous-requĂȘte dans le FROM (table dĂ©rivĂ©e)
Tu peux utiliser un SELECT comme source de donnĂ©es dans le FROM, comme si c’Ă©tait une table. On appelle ça une table dĂ©rivĂ©e (ou inline view).
SELECT sub.nom, sub.ca
FROM (
SELECT c.nom, SUM(co.montant) AS ca
FROM clients c
INNER JOIN commandes co ON c.id = co.client_id
GROUP BY c.nom
) AS sub
WHERE sub.ca > 500;
— Comparer chaque vendeur Ă la moyenne gĂ©nĂ©rale
SELECT v.vendeur, v.ca, moy.ca_moyen,
v.ca – moy.ca_moyen AS ecart
FROM (
SELECT vendeur, SUM(montant) AS ca
FROM ventes GROUP BY vendeur
) AS v,
(
SELECT AVG(total) AS ca_moyen
FROM (SELECT SUM(montant) AS total FROM ventes GROUP BY vendeur) t
) AS moy;
La table dĂ©rivĂ©e doit avoir un alias (AS sub). Sans alias, MySQL, PostgreSQL et SQL Server renvoient une erreur. L’alternative moderne : utiliser un CTE (WITH).
Sous-requĂȘte dans le SELECT
SELECT produit, montant,
(SELECT AVG(montant) FROM commandes) AS moyenne,
montant – (SELECT AVG(montant) FROM commandes) AS ecart
FROM commandes;
— Nombre de commandes par client (sous-requĂȘte corrĂ©lĂ©e)
SELECT c.nom,
(SELECT COUNT(*) FROM commandes co
WHERE co.client_id = c.id) AS nb_commandes
FROM clients c;
La sous-requĂȘte dans le SELECT est corrĂ©lĂ©e quand elle rĂ©fĂ©rence la requĂȘte principale (co.client_id = c.id). Elle est exĂ©cutĂ©e pour chaque ligne â potentiellement lente sur de grandes tables. PrĂ©fĂšre un LEFT JOIN + GROUP BY pour la performance.
EXISTS / NOT EXISTS
EXISTS retourne TRUE si la sous-requĂȘte retourne au moins une ligne. C’est une sous-requĂȘte corrĂ©lĂ©e â elle dĂ©pend de la requĂȘte principale.
SELECT * FROM clients c
WHERE EXISTS (
SELECT 1 FROM commandes co
WHERE co.client_id = c.id
);
— Clients qui n’ont JAMAIS commandĂ© (mieux que NOT IN)
SELECT * FROM clients c
WHERE NOT EXISTS (
SELECT 1 FROM commandes co
WHERE co.client_id = c.id
);
— CatĂ©gories avec au moins un produit en stock
SELECT * FROM categories cat
WHERE EXISTS (
SELECT 1 FROM produits p
WHERE p.categorie_id = cat.id AND p.stock > 0
);
NOT EXISTS est plus sĂ»r que NOT IN car il gĂšre correctement les NULL. C’est la mĂ©thode recommandĂ©e pour trouver les lignes « sans correspondance ». SELECT 1 est une convention â le contenu du SELECT dans EXISTS n’a pas d’importance, seule l’existence de lignes compte.
Sous-requĂȘte vs JOIN
| CritĂšre | Sous-requĂȘte | JOIN |
|---|---|---|
| Lisibilité | Logique étape par étape | Plus compact pour les cas simples |
| Performance | â ïž CorrĂ©lĂ©es = lentes (N+1) | â GĂ©nĂ©ralement plus rapide |
| Colonnes multiples | Une seule colonne retournée | AccÚs à toutes les colonnes des deux tables |
| Filtrage d’existence | EXISTS / NOT EXISTS | LEFT JOIN ⊠IS NULL |
| Calculs intermĂ©diaires | â IdĂ©al (table dĂ©rivĂ©e, CTE) | Moins naturel |
— Version sous-requĂȘte
SELECT * FROM clients
WHERE id IN (SELECT client_id FROM commandes);
— Version JOIN
SELECT DISTINCT c.*
FROM clients c
INNER JOIN commandes co ON c.id = co.client_id;
— Version EXISTS (recommandĂ©e pour les gros volumes)
SELECT * FROM clients c
WHERE EXISTS (SELECT 1 FROM commandes co WHERE co.client_id = c.id);
En pratique : utilise JOIN pour combiner les donnĂ©es de plusieurs tables. Utilise les sous-requĂȘtes pour les calculs intermĂ©diaires (moyennes, max, agrĂ©gats dans WHERE). Utilise EXISTS pour tester l’existence. Les optimiseurs modernes (PostgreSQL, MySQL 8+) réécrivent souvent les sous-requĂȘtes en JOIN automatiquement.
Erreurs fréquentes
| Erreur | ProblĂšme | Solution |
|---|---|---|
| Sous-requĂȘte retourne plusieurs lignes avec = | Erreur : subquery returns more than 1 row | Utiliser IN au lieu de = |
| NOT IN avec des NULL | Retourne 0 résultats | Utiliser NOT EXISTS |
| Oublier l’alias dans le FROM | Erreur de syntaxe | Ajouter AS nom aprĂšs la sous-requĂȘte |
| Sous-requĂȘte corrĂ©lĂ©e lente | ExĂ©cutĂ©e pour chaque ligne (N+1) | Réécrire en JOIN + GROUP BY |
| Sous-requĂȘte trop imbriquĂ©e | Code illisible et difficile Ă dĂ©bugger | Utiliser des CTE (WITH) |
Questions fréquentes
đ Jointures SQL
đ GROUP BY & HAVING
âïž WHERE vs HAVING
đą Fonctions d’agrĂ©gation
đ UNION vs UNION ALL
đïž Cours SQL complet
đ Hub Programmation
Les sous-requĂȘtes SQL â SELECT imbriquĂ©s
RĂ©fĂ©rence : sql.sh Sous-requĂȘtes



































