Les SELECT imbriqués¶
Les SELECT imbriqués, aussi appelés sous‑requêtes ou subqueries, sont des requêtes SQL placées à l’intérieur d’une autre requête.
Elles permettent d’effectuer des calculs, filtrages ou comparaisons dépendant d’autres résultats.
Elles sont très utiles quand une jointure (JOIN) ou un filtre (WHERE ou HAVING) ne suffit pas.
Syntaxe¶
Un SELECT imbriqué est :
- placé entre parenthèses
- exécuté avant la requête principale
- utilisé dans une clause WHERE, HAVING, FROM ou parfois SELECT
Un SELECT imbriqué doit :
- être entre parenthèses
- retourner un résultat compatible avec l’opérateur de comparaison
- être adapté au nombre de lignes retournées
- avoir un alias s’il est dans le FROM
Exemple :
SELECT colonnes
FROM table_principale
WHERE colonne & opérateur (
SELECT colonne
FROM autre_table
WHERE condition
);
👉 La sous-requête est écrite entre parenthèses
👉 Elle doit retourner un type compatible avec la comparaison
Adapter l'opérateur de comparaison
| La sous-requête retourne | Utiliser |
|---|---|
| 1 valeur | =, >, <, >=, <= , LIKE |
| Plusieurs valeurs | IN |
| Tester existence | EXISTS |
Cas classique d'utilisation #1 : dans le WHERE¶
✔ Un cas classique d'utilisation du SELECT imbriqué est la comparaison à un résultat d'un agrégat.
Supposons l'énoncé suivant :
Trouver les employés gagnant plus que la moyenne de tous les employés.
❌ Erreur #1¶
SELECT nomEmploye, salaireEmploye
FROM employe
WHERE salaireEmploye > avg(salaireEmploye); --ERREUR
Message d'erreur : ORA-00934: group function is not allowed here
En effet, il faut se rappeler l'ordre d'exécution :
- FROM
- JOIN
- WHERE
- GROUP BY
- HAVING
- SELECT
- ORDER BY
👉 La clause WHERE est exécutée avant le regroupement (GROUP BY).
👉 Les agrégats sont calculés après le regroupement.
Donc au moment où WHERE s’exécute… l’agrégat n’existe pas encore !
❌ Erreur #2¶
SELECT nomEmploye, salaireEmploye
FROM employe
GROUP BY nomEmploye, salaireEmploye --PROBLÈME : regroupement par tuple
HAVING salaireEmploye > avg(salaireEmploye);
✅ Syntaxiquement valide sous Oracle, donc pas d'erreur
❌ Logiquement inutile / incorrect, car :
👉 On regroupe par tuple (nomEmploye, salaireEmploye)
👉 Chaque employé devient son propre groupe
👉 Chaque groupe contient… une seule ligne
Donc, dans chaque groupe, l'énoncé AVG(salaireEmploye) représentement tout simplementn le salaire de l'employé lui-même !
Dans ce cas, salaireEmploye > salaireEMploye est toujours FAUX !
✅ Solution : utiliser une sous-requête¶
Rappel : on veut les employés qui gagnent plus que la moyenne globale.
On doit :
-
Connaître la moyenne globale
SELECT AVG(salaireEmploye) FROM employe; -
Extraire les employés dont le salaire est supérieur à la moyenne calculée
SELECT nomEmploye, salaireEmploye FROM employe WHERE salaireEmploye > (moyenne calculée);
✔ Solution :
SELECT nomEmploye, salaireEmploye
FROM employe
WHERE salaireEmploye > (
SELECT AVG(salaireEmploye)
FROM employe
);
Cas classique d'utilisation #2 : dans le FROM ou le JOIN¶
Il existe des situations où un SELECT imbriqué dans le FROM est vraiment nécessaire, notamment lorsqu’on calcule un agrégat intermédiaire qui sert ensuite à filtrer ou à transformer les lignes, mais sans perdre le détail des lignes originales.
Dans ces cas, un simple JOIN ne suffit pas.
Supposons l'énoncé suivant :
On veut comparer le salaire de chaque employé à la moyenne de salaire de leur département respectif.
Analyse¶
On veut donc :
- Conserver les données de chaque employé
- Ajouter la colonne de moyenne pour leur département
Requête principale : afficher les employés
Ajout de colonne : moyenne du département
✔ On se rappelle que l'ajout de colonne se fait à l'aide d'une jointure.
❌ Erreur¶
SELECT e.nomEmploye, e.salaireEmploye
FROM employe e
JOIN employe e2
ON e.idDepartement = e2.idDepartement
WHERE e.salaireEmploye > AVG(e2.salaireEmploye); -- ❌ ERREUR
On a vu qu'une fonction d'agrégat ne peut être utilisée dans un WHERE puisque les données ne sont pas encore groupées !
✅ Solution : SELECT en jointure¶
Donc pour afficher tous les employés avec la moyenne de salaire de leur département, on doit :
-
Connaître la moyenne de salaire par département
SELECT idDepartementEmploye, AVG(salaireEmploye) AS moyenne_salaire FROM employe GROUP BY idDepartementEmploye; -
Joindre ces données à la table employé pour le bon numéro de département
SELECT nomEmploye, salaireEmploye, moyenne_salaire FROM employe JOIN ( Moyenne de salaire par département ) moyDept ON employe.idDepartementEmploye = moyDept.idDepartementEmploye;
✔ Solution :
SELECT nomEmploye,
salaireEmploye,
moyenne_salaire
FROM employe
JOIN (
SELECT idDepartementEmploye,
AVG(salaireEmploye) AS moyenne_salaire
FROM employe
GROUP BY idDepartementEmploye;
) moyDept
ON employe.idDepartementEmploye = moyDept.idDepartementEmploye;