Tutoriel sur les fonctions de fenêtrage SQL
Pour des analyses avancées (ROW_NUMBER, RANK, LEAD, LAG).
Maîtrisez les sous-requêtes dans WHERE, FROM, SELECT, les sous-requêtes corrélées et leur utilisation avec INSERT, UPDATE, DELETE. Exemples concrets et bonnes pratiques.
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.
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.

Filtrer les résultats en fonction d'une condition dynamique ou comparer une valeur avec un résultat calculé.
-- Tables : Clients(ClientID, Nom), Commandes(OrderID, ClientID, Date)
SELECT ClientID, Nom
FROM Clients
WHERE ClientID IN (SELECT DISTINCT ClientID FROM Commandes);
-- Table : Produits(ProduitID, Nom, Prix)
SELECT ProduitID, Nom, Prix
FROM Produits
WHERE Prix > (SELECT AVG(Prix) FROM Produits);
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 (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);
Créer une table temporaire (dérivée) pour simplifier une requête complexe ou pré-agréger des données.
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;
Retourner une valeur calculée pour chaque ligne (sous-requête scalaire).
SELECT c.ClientID, c.Nom,
(SELECT COUNT(*)
FROM Commandes o
WHERE o.ClientID = c.ClientID) AS NombreCommandes
FROM Clients c;
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.
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
);
AVG() OVER(PARTITION BY DépartementID)INSERT INTO Clients (ClientID, Nom)
SELECT ClientID, Nom
FROM NouveauxClients
WHERE ClientID NOT IN (SELECT ClientID FROM Clients);
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
);
DELETE FROM Clients
WHERE ClientID NOT IN (SELECT DISTINCT ClientID FROM Commandes);
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
);
-- 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;
| 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 |
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.
Au-delà de 3-4 niveaux, la requête devient illisible. Utilisez des CTE (Common Table Expressions) pour clarifier.
Donnez des noms clairs aux sous-requêtes : AS CommandesParClient plutôt que AS t.
Assurez-vous que chaque sous-requête fonctionne seule avant de l'intégrer. Cela facilite le débogage.
NOT IN (SELECT ...) peut retourner UNKNOWN si la sous-requête contient des NULL. NOT EXISTS est plus sûr.
-- 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);
EXPLAIN pour analyser le plan d'exécution.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.
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).
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;
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.
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.
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.
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.
EXISTS est souvent plus performant que IN.NOT IN avec des NULL (préférez NOT EXISTS).