Sommaire
- 1. Introduction aux sous-requêtes
- 2. Sous-requête dans la clause WHERE
- 3. Sous-requête dans la clause FROM
- 4. Sous-requête dans la clause SELECT
- 5. Sous-requêtes corrélées
- 6. Sous-requêtes dans INSERT, UPDATE, DELETE
- 7. Requêtes complexes avec sous-requêtes
- 8. Sous-requêtes vs JOIN : quand utiliser quoi ?
- 9. Bonnes pratiques
- 10. Impact sur les performances
- 11. FAQ
- Conclusion
- Articles connexes
1. Introduction aux sous-requêtes
Les sous-requêtes (ou requêtes imbriquées) en SQL sont des requêtes incluses dans une autre requête. Elles permettent de réaliser des opérations complexes en décomposant un problème en plusieurs étapes.
Définition
Une sous-requête est une requête SQL placée à l’intérieur d’une autre requête (SELECT, INSERT, UPDATE, DELETE). Elle peut être utilisée dans les clauses SELECT, FROM, WHERE, HAVING.
Types de sous-requêtes
- Sous-requête dans WHERE : Filtrer les résultats en fonction d’une condition dynamique.
- Sous-requête dans FROM : Créer une table temporaire (dérivée).
- Sous-requête dans SELECT : Retourner une valeur calculée par ligne.
- Sous-requête corrélée : Dépend de la requête externe (exécutée ligne par ligne).
85%
des requêtes complexes utilisent au moins une sous-requête
Analyse logs, 2025
3x
plus lisible qu’une requête monolithique
Stack Overflow Survey, 2026

2. Sous-requête dans la clause WHERE
Cas d’utilisation :
Filtrer les résultats en fonction d’une condition dynamique ou comparer une valeur avec un résultat calculé.
Exemple 1 : Trouver les clients ayant passé une commande
IN avec sous-requête
-- Tables : Clients(ClientID, Nom), Commandes(OrderID, ClientID, Date)
SELECT ClientID, Nom
FROM Clients
WHERE ClientID IN (SELECT DISTINCT ClientID FROM Commandes);Exemple 2 : Trouver les produits dont le prix est supérieur à la moyenne
Comparaison avec une valeur scalaire
-- Table : Produits(ProduitID, Nom, Prix)
SELECT ProduitID, Nom, Prix
FROM Produits
WHERE Prix > (SELECT AVG(Prix) FROM Produits);Opérateurs utilisables avec sous-requêtes WHERE
IN/NOT IN: valeur présente dans la liste retournéeEXISTS/NOT EXISTS: existence d’au moins une ligne>,<,=,>=,<=: comparaison avec une valeur scalaireANY/SOME: comparaison avec au moins une valeurALL: comparaison avec toutes les valeurs
EXISTS vs IN
-- EXISTS (souvent plus performant pour les grandes tables)
SELECT * FROM Clients c
WHERE EXISTS (SELECT 1 FROM Commandes o WHERE o.ClientID = c.ClientID);
-- IN (plus lisible pour les petites listes)
SELECT * FROM Clients
WHERE ClientID IN (1, 2, 3, 4, 5);3. Sous-requête dans la clause FROM
Cas d’utilisation :
Créer une table temporaire (dérivée) pour simplifier une requête complexe ou pré-agréger des données.
Exemple : Calculer le revenu total par client
Table dérivée avec GROUP BY
SELECT c.ClientID, c.Nom, COALESCE(TotalRevenu, 0) AS TotalRevenu
FROM Clients c
LEFT JOIN (
SELECT ClientID, SUM(Montant) AS TotalRevenu
FROM Commandes
GROUP BY ClientID
) AS RevenuClients
ON c.ClientID = RevenuClients.ClientID;Astuce : Les sous-requêtes dans FROM sont aussi appelées “tables dérivées”. Elles sont indispensables quand vous devez agréger avant de joindre.
4. Sous-requête dans la clause SELECT
Cas d’utilisation :
Retourner une valeur calculée pour chaque ligne (sous-requête scalaire).
Exemple : Afficher le nombre de commandes par client
Sous-requête scalaire dans SELECT
SELECT c.ClientID, c.Nom,
(SELECT COUNT(*)
FROM Commandes o
WHERE o.ClientID = c.ClientID) AS NombreCommandes
FROM Clients c;Contrainte : Une sous-requête dans SELECT doit retourner une seule valeur (une ligne, une colonne). Si elle retourne plusieurs lignes, la requête échoue.
5. Sous-requêtes corrélées
Définition :
Une sous-requête corrélée dépend de la requête externe. Elle est exécutée une fois pour chaque ligne de la requête principale.
Exemple : Employés avec salaire > moyenne de leur département
Sous-requête corrélée
SELECT e.EmployéID, e.Nom, e.Salaire, e.DépartementID
FROM Employés e
WHERE e.Salaire > (
SELECT AVG(Salaire)
FROM Employés e2
WHERE e2.DépartementID = e.DépartementID -- Corrélation ici
);Attention
Les sous-requêtes corrélées peuvent être lentes sur les grandes tables
Car exécutées ligne par ligne
Info
Alternative : fonction de fenêtrage (si SGBD supporte)
AVG() OVER(PARTITION BY DépartementID)
6. Sous-requêtes dans INSERT, UPDATE, DELETE
Exemple 1 : INSERT avec sous-requête
Insérer les nouveaux clients (sans doublon)
INSERT INTO Clients (ClientID, Nom)
SELECT ClientID, Nom
FROM NouveauxClients
WHERE ClientID NOT IN (SELECT ClientID FROM Clients);Exemple 2 : UPDATE avec sous-requête corrélée
Augmenter les salaires inférieurs à la moyenne du département
UPDATE Employés e
SET Salaire = Salaire * 1.10
WHERE Salaire < (
SELECT AVG(Salaire)
FROM Employés e2
WHERE e2.DépartementID = e.DépartementID
);Exemple 3 : DELETE avec sous-requête
Supprimer les clients sans commande
DELETE FROM Clients
WHERE ClientID NOT IN (SELECT DISTINCT ClientID FROM Commandes);Attention : Sur MySQL, vous ne pouvez pas modifier une table et utiliser une sous-requête qui la référence dans la même requête (erreur 1093). Utilisez une table temporaire ou une jointure.
7. Requêtes complexes avec sous-requêtes
Exemple : Clients ayant passé plus de commandes que la moyenne
Double sous-requête (imbriquée)
SELECT c.ClientID, c.Nom, COUNT(o.OrderID) AS NombreCommandes
FROM Clients c
JOIN Commandes o ON c.ClientID = o.ClientID
GROUP BY c.ClientID, c.Nom
HAVING COUNT(o.OrderID) > (
SELECT AVG(NombreCommandes)
FROM (
SELECT COUNT(OrderID) AS NombreCommandes
FROM Commandes
GROUP BY ClientID
) AS CommandesParClient
);Exemple : Classement des clients par chiffre d’affaires
Sous-requête dans SELECT avec TOP/LIMIT
-- Top 5 clients (SQL Server)
SELECT TOP 5 ClientID, SUM(Montant) as CA
FROM Commandes
GROUP BY ClientID
ORDER BY CA DESC;
-- MySQL / PostgreSQL
SELECT ClientID, SUM(Montant) as CA
FROM Commandes
GROUP BY ClientID
ORDER BY CA DESC
LIMIT 5;8. Sous-requêtes vs JOIN : quand utiliser quoi ?
| Situation | JOIN | Sous-requête |
|---|---|---|
| Filtrer sur existence (IN/EXISTS) | Possible mais moins intuitif✅ Plus lisible | |
| Joindre après agrégation | ✅ Table dérivée dans FROM ✅ Aussi possible | |
| Ajouter une colonne calculée par ligne | ❌ Nécessite GROUP BY ✅ Sous-requête scalaire dans SELECT | |
| Performance sur grandes tables | ✅ Généralement meilleur ⚠️ Corrélées peuvent être lentes |
Règle simple :
- JOIN : quand vous avez besoin de colonnes des deux tables.
- Sous-requête : quand vous avez besoin d’un filtre conditionnel ou d’une valeur calculée.
- EXISTS : souvent plus performant que IN pour les grandes tables.
9. Bonnes pratiques
1. Évitez les sous-requêtes inutiles
Préférez les JOIN quand vous avez besoin de colonnes des deux tables. Les sous-requêtes sont idéales pour les filtres et les calculs, moins pour les jointures.
2. Limitez la profondeur d’imbrication
Au-delà de 3-4 niveaux, la requête devient illisible. Utilisez des CTE (Common Table Expressions) pour clarifier.
3. Utilisez des alias explicites
Donnez des noms clairs aux sous-requêtes : AS CommandesParClient plutôt que AS t.
4. Testez les sous-requêtes séparément
Assurez-vous que chaque sous-requête fonctionne seule avant de l’intégrer. Cela facilite le débogage.
5. Préférez EXISTS à IN quand la sous-requête peut retourner NULL
NOT IN (SELECT ...) peut retourner UNKNOWN si la sous-requête contient des NULL. NOT EXISTS est plus sûr.
IN vs EXISTS avec NULL
-- DANGEREUX : si sous_requête contient NULL, NOT IN retourne UNKNOWN (aucune ligne)
DELETE FROM Clients WHERE ClientID NOT IN (SELECT ClientID FROM Commandes);
-- SÉCURISÉ : NOT EXISTS gère correctement les NULL
DELETE FROM Clients c
WHERE NOT EXISTS (SELECT 1 FROM Commandes o WHERE o.ClientID = c.ClientID);10. Impact sur les performances
Bonnes pratiques de performance :
- Les sous-requêtes non corrélées sont exécutées une fois → rapides.
- Les sous-requêtes corrélées sont exécutées N fois → peuvent être lentes sur les grandes tables (N = nb de lignes de la table externe).
- Utilisez
EXPLAINpour analyser le plan d’exécution. - Sur les très gros volumes, transformez les sous-requêtes corrélées en JOIN ou CTE.
FAQ
Quelle est la différence entre IN et EXISTS ?
IN compare une valeur à une liste (souvent une sous-requête). EXISTS teste l'existence d'au moins une ligne. EXISTS est souvent plus performant car il s'arrête dès qu'une ligne est trouvée. De plus, NOT EXISTS gère correctement les NULL, contrairement à NOT IN.
Une sous-requête peut-elle retourner plusieurs colonnes ?
Oui, mais uniquement dans certaines clauses. Dans FROM, une sous-requête peut retourner plusieurs colonnes. Dans WHERE avec IN, elle ne peut retourner qu'une colonne. Dans SELECT, elle doit retourner une seule valeur (scalaire).
Comment transformer une sous-requête en CTE ?
Les CTE (Common Table Expressions) rendent les requêtes plus lisibles :WITH CommandesParClient AS (SELECT ClientID, COUNT(*) as Nb FROM Commandes GROUP BY ClientID) SELECT * FROM Clients c JOIN CommandesParClient cp ON c.ClientID = cp.ClientID;
Les sous-requêtes sont-elles supportées dans tous les SGBD ?
Oui, les sous-requêtes sont standard SQL et supportées par tous les SGBD modernes (MySQL, PostgreSQL, SQL Server, Oracle, SQLite). Seules les fonctions de fenêtrage (qui remplacent parfois les sous-requêtes corrélées) ne sont pas disponibles partout.
Comment déboguer une sous-requête complexe ?
Exécutez la sous-requête seule pour vérifier son résultat. Utilisez des alias temporaires. Commentez les parties pour isoler l'erreur. Les CTE permettent de tester chaque bloc indépendamment.
Quand utiliser ALL et ANY ?
ALL : condition vraie pour toutes les valeurs (ex: Prix > ALL (SELECT Prix FROM Produits WHERE Categorie = 'Luxe')). ANY (ou SOME) : condition vraie pour au moins une valeur. Ces opérateurs sont moins courants mais utiles pour des comparaisons avancées.
Conclusion
Les sous-requêtes sont un outil puissant pour décomposer des problèmes complexes en étapes simples. Elles peuvent être utilisées dans WHERE, FROM, SELECT, HAVING et dans les opérations INSERT, UPDATE, DELETE.
À retenir
- Les sous-requêtes non corrélées sont exécutées une seule fois.
- Les sous-requêtes corrélées sont exécutées ligne par ligne (attention aux performances).
EXISTSest souvent plus performant queIN.- Préférez les CTE pour les requêtes très imbriquées.
- Testez chaque sous-requête séparément avant intégration.
- Attention aux
NOT INavec des NULL (préférezNOT EXISTS).
Pour aller plus loin : Découvrez notre tutoriel sur les fonctions de fenêtrage SQL pour des analyses encore plus avancées.
Faites parler vos données
Apprenez les méthodes et les outils pour extraire de la valeur stratégique : Data Science : Le guide complet des méthodes et outils.