Aller au contenu

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 :

  1. être entre parenthèses
  2. retourner un résultat compatible avec l’opérateur de comparaison
  3. être adapté au nombre de lignes retournées
  4. 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 :

  1. FROM
  2. JOIN
  3. WHERE
  4. GROUP BY
  5. HAVING
  6. SELECT
  7. 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 :

  1. Connaître la moyenne globale

    SELECT AVG(salaireEmploye)
    FROM employe;
    
  2. 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 :

  1. Connaître la moyenne de salaire par département

    SELECT idDepartementEmploye,
           AVG(salaireEmploye) AS moyenne_salaire
    FROM employe
    GROUP BY idDepartementEmploye;
    
  2. 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;