Sous-requêtes et requêtes imbriquées
Sous-requête dans WHERE (valeur unique, IN, NOT IN). Sous-requête corrélée. Sous-requête dans FROM (table dérivée). EXISTS / NOT EXISTS. Quand utiliser sous-requête vs JOIN.
Concepts Théoriques
Une sous-requête est une requête SELECT à l'intérieur d'une autre requête. Elle permet de résoudre des problèmes en deux étapes logiques : d'abord calculer une valeur ou une liste, puis l'utiliser comme filtre ou comme source de données. C'est comme poser une question préalable avant de poser la vraie question.
Sous-requête dans WHERE — filtrer avec un résultat calculé
Avec une valeur unique — la sous-requête retourne UN seul nombre, utilisé comme seuil : SELECT name, price FROM products WHERE price > (SELECT AVG(price) FROM products); — "les produits dont le prix dépasse la moyenne".
Avec une liste (IN) — la sous-requête retourne plusieurs valeurs : SELECT name FROM products WHERE category_id IN (SELECT id FROM categories WHERE is_active = TRUE); — "les produits des catégories actives".
NOT IN pour l'inverse : WHERE id NOT IN (SELECT DISTINCT product_id FROM order_items) — "les produits jamais commandés".
Sous-requête corrélée — la plus puissante
Une sous-requête corrélée fait référence à la requête externe. Elle s'exécute pour CHAQUE ligne de la requête principale :
SELECT p.name, p.price FROM products p WHERE p.price > (SELECT AVG(p2.price) FROM products p2 WHERE p2.category_id = p.category_id);
Pour chaque produit, MySQL calcule la moyenne de SA catégorie, puis compare. Le résultat : "les produits plus chers que la moyenne de leur catégorie" — une analyse impossible avec un simple WHERE.
Comment penser une sous-requête corrélée : imaginez que MySQL parcourt chaque produit un par un. Pour le wax (catégorie 1), il calcule la moyenne des produits de la catégorie 1. Pour le Samsung (catégorie 2), il calcule la moyenne de la catégorie 2. Et il compare à chaque fois.
Sous-requête dans FROM — table dérivée
La sous-requête crée une "table temporaire" que vous pouvez requêter comme une vraie table :
SELECT categorie, nb FROM (SELECT c.name AS categorie, COUNT(p.id) AS nb FROM categories c LEFT JOIN products p ON p.category_id = c.id GROUP BY c.id) AS stats WHERE nb > 3;
Le AS stats est obligatoire — MySQL exige un alias pour les tables dérivées.
EXISTS / NOT EXISTS — vérifier l'existence
EXISTS retourne TRUE dès qu'il trouve au moins UNE ligne correspondante. C'est souvent plus performant que IN pour les grosses tables, parce que EXISTS s'arrête au premier résultat trouvé alors que IN charge la liste entière en mémoire.
SELECT first_name, last_name FROM customers cu WHERE NOT EXISTS (SELECT 1 FROM orders o WHERE o.customer_id = cu.id); — "les clients qui n'ont jamais commandé".
Le SELECT 1 est une convention — on se fiche de ce qui est retourné, on vérifie juste si ça existe.
Sous-requête vs JOIN — quand utiliser quoi
Beaucoup de sous-requêtes peuvent être réécrites en JOIN. Règle pratique : utilisez un JOIN quand vous avez besoin de colonnes des deux tables dans le résultat. Utilisez une sous-requête quand vous avez besoin d'une valeur calculée pour filtrer. En performance, les JOINs sont souvent plus rapides, mais MySQL optimise bien les sous-requêtes simples. Privilégiez toujours la lisibilité.