FicheSql sous requetes

Les Sous-RequĂȘtes SQL

Imbriquer des SELECT — dans WHERE, FROM, SELECT, avec IN, EXISTS, ANY et ALL

9
Sections
20+
Exemples
SQL
Standard

SECTION 01

C’est quoi une sous-requĂȘte ?

đŸȘ† Un SELECT dans un SELECT

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.

— RequĂȘte simple : quel est le montant moyen ?
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.

SECTION 02

Sous-requĂȘte dans le WHERE

🎯 Sous-requĂȘte scalaire (retourne UNE valeur)
— Commandes supĂ©rieures Ă  la moyenne
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.

SECTION 03

Sous-requĂȘte avec IN / NOT IN

📋 Retourne une liste de valeurs
— Clients qui ont passĂ© au moins une commande
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.

SECTION 04

Sous-requĂȘte dans le FROM (table dĂ©rivĂ©e)

📩 CrĂ©er une « table temporaire »

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).

— Le CA par client, puis filtrer les top clients
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).

SECTION 05

Sous-requĂȘte dans le SELECT

📊 Ajouter une colonne calculĂ©e
— Chaque commande + la moyenne globale
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.

SECTION 06

EXISTS / NOT EXISTS

✅ Tester l’existence de lignes

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.

— Clients qui ont au moins une commande
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.

SECTION 07

Sous-requĂȘte vs JOIN

CritĂšreSous-requĂȘteJOIN
LisibilitéLogique étape par étapePlus compact pour les cas simples
Performance⚠ CorrĂ©lĂ©es = lentes (N+1)✅ GĂ©nĂ©ralement plus rapide
Colonnes multiplesUne seule colonne retournéeAccÚs à toutes les colonnes des deux tables
Filtrage d’existenceEXISTS / NOT EXISTSLEFT JOIN 
 IS NULL
Calculs intermĂ©diaires✅ IdĂ©al (table dĂ©rivĂ©e, CTE)Moins naturel
— MÊME RÉSULTAT — clients avec commandes

— 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.

SECTION 08

Erreurs fréquentes

ErreurProblĂšmeSolution
Sous-requĂȘte retourne plusieurs lignes avec =Erreur : subquery returns more than 1 rowUtiliser IN au lieu de =
NOT IN avec des NULLRetourne 0 résultatsUtiliser NOT EXISTS
Oublier l’alias dans le FROMErreur de syntaxeAjouter AS nom aprĂšs la sous-requĂȘte
Sous-requĂȘte corrĂ©lĂ©e lenteExĂ©cutĂ©e pour chaque ligne (N+1)Réécrire en JOIN + GROUP BY
Sous-requĂȘte trop imbriquĂ©eCode illisible et difficile Ă  dĂ©buggerUtiliser des CTE (WITH)

SECTION 09

Questions fréquentes

C’est quoi une sous-requĂȘte corrĂ©lĂ©e ?
C’est une sous-requĂȘte qui rĂ©fĂ©rence la requĂȘte principale (ex : WHERE co.client_id = c.id). Elle est exĂ©cutĂ©e pour chaque ligne de la requĂȘte externe, ce qui la rend plus lente qu’une sous-requĂȘte indĂ©pendante. EXISTS et les sous-requĂȘtes dans le SELECT sont souvent corrĂ©lĂ©es.
C’est quoi un CTE (WITH) ?
Un Common Table Expression : WITH nom AS (SELECT 
) SELECT 
 FROM nom. C’est une alternative lisible aux sous-requĂȘtes dans le FROM. Le CTE est nommĂ©, rĂ©utilisable dans la requĂȘte, et plus facile Ă  lire que des sous-requĂȘtes imbriquĂ©es. SupportĂ© par MySQL 8+, PostgreSQL, SQL Server et Oracle.
Peut-on imbriquer des sous-requĂȘtes Ă  l’infini ?
Techniquement oui (la limite dĂ©pend du SGBD — 255 niveaux en SQL Server par exemple). En pratique, au-delĂ  de 2 niveaux, le code devient illisible. Utilise des CTE pour structurer les requĂȘtes complexes.
IN vs EXISTS — lequel est plus rapide ?
Ça dĂ©pend du volume de donnĂ©es. EXISTS s’arrĂȘte dĂšs qu’il trouve une correspondance (court-circuit). IN Ă©value toute la liste. Sur une petite sous-requĂȘte, IN est souvent aussi rapide. Sur de gros volumes ou avec des index, EXISTS est gĂ©nĂ©ralement meilleur. Les optimiseurs modernes les rendent souvent Ă©quivalents.

Les sous-requĂȘtes SQL — SELECT imbriquĂ©s

RĂ©fĂ©rence : sql.sh Sous-requĂȘtes