Maintenez SQLtest en vie : Aidez à couvrir les coûts du serveur de ce mois-ci de 100 $ pour garder toutes les leçons et le terrain de jeu SQL en ligne. Soutenez le projet →
Code SQL copié dans le presse-papiers

Leçon 6.2 · Temps de lecture: ~8 minutes

Une sous-requête dans WHERE permet de filtrer les lignes en s'appuyant sur le résultat intermédiaire d'une autre requête. Dans cette leçon, vous verrez quand utiliser les opérateurs de comparaison, IN, NOT IN, EXISTS, NOT EXISTS, ANY et ALL, et comment choisir l'option la plus sûre en pratique.

Sous-requêtes dans la clause WHERE

Dans la leçon précédente, nous avons vu l'idée générale des sous-requêtes. Ici, nous nous concentrons sur le cas le plus fréquent: le filtrage dans WHERE, lorsque la requête externe dépend de valeurs calculées dynamiquement.

En pratique, c'est utile partout: de la recherche de clients sans paiements jusqu'à la comparaison d'une ligne avec un ensemble de résultats.

Schéma de sous-requêtes dans WHERE avec les opérateurs IN, EXISTS, ANY et ALL

Sous-requêtes scalaires et opérateurs de comparaison

Si une sous-requête retourne exactement une valeur, on parle de sous-requête scalaire. Dans ce cas, vous pouvez utiliser les opérateurs classiques =, <>, >, >=, <, <=.

Scénario: trouver les acteurs qui ont le même prénom que l'acteur avec actor_id = 10.

SELECT
    first_name,
    last_name
FROM
    actor
WHERE
    first_name = (
        SELECT first_name
        FROM actor
        WHERE actor_id = 10
    )
    AND actor_id <> 10;

Remarque: si la requête interne retourne plusieurs lignes, la requête échoue avec une erreur.


Sous-requêtes multi-lignes: IN et NOT IN

Quand une sous-requête retourne une liste de valeurs (une colonne, plusieurs lignes), utilisez IN.

Scénario: trouver les films de la catégorie Action.

SELECT
    f.title
FROM
    film AS f
WHERE
    f.film_id IN (
        SELECT
            fc.film_id
        FROM
            film_category AS fc
        WHERE
            fc.category_id = (
                SELECT
                    c.category_id
                FROM
                    category AS c
                WHERE
                    c.name = 'Action'
            )
    );

Résultat: vous obtenez tous les films reliés à la catégorie Action via la table film_category.

NOT IN fait le filtrage inverse, mais gardez un point important en tête: si le résultat de la sous-requête contient NULL, la condition peut produire un résultat vide inattendu. Dans ce cas, NOT EXISTS est souvent plus fiable.


Vérification d'existence: EXISTS et NOT EXISTS

EXISTS vérifie la présence d'au moins une ligne dans la sous-requête. Le moteur peut s'arrêter dès la première correspondance, donc cette approche est souvent efficace sur de grandes tables.

EXISTS

Scénario: trouver les clients qui ont au moins un paiement.

SELECT
    c.first_name,
    c.last_name
FROM
    customer AS c
WHERE
    EXISTS (
        SELECT
            1
        FROM
            payment AS p
        WHERE
            p.customer_id = c.customer_id
    );

Remarque: avec EXISTS, on écrit souvent SELECT 1, car seul le fait qu'une ligne existe est important.

NOT EXISTS

Scénario: trouver les clients qui n'ont effectué aucun paiement.

SELECT
    c.first_name,
    c.last_name
FROM
    customer AS c
WHERE
    NOT EXISTS (
        SELECT
            1
        FROM
            payment AS p
        WHERE
            p.customer_id = c.customer_id
    );

Résultat: seules les lignes de clients sans aucune entrée correspondante dans payment sont retournées.


Comparaison avec un ensemble: ANY et ALL

  • ANY: la condition est vraie si elle est satisfaite pour au moins une valeur de la sous-requête.
  • ALL: la condition est vraie uniquement si elle est satisfaite pour toutes les valeurs de la sous-requête.

Scénario: comparer la durée d'un film avec les durées des films de la catégorie Comedy.

SELECT
    f.title,
    f.length
FROM
    film AS f
WHERE
    f.length > ANY (
        SELECT
            f2.length
        FROM
            film AS f2
        INNER JOIN film_category AS fc ON f2.film_id = fc.film_id
        INNER JOIN category AS c ON fc.category_id = c.category_id
        WHERE
            c.name = 'Comedy'
    );

Résultat: le film est retenu s'il est plus long qu'au moins un film de Comedy.

SELECT
    f.title,
    f.length
FROM
    film AS f
WHERE
    f.length > ALL (
        SELECT
            f2.length
        FROM
            film AS f2
        INNER JOIN film_category AS fc ON f2.film_id = fc.film_id
        INNER JOIN category AS c ON fc.category_id = c.category_id
        WHERE
            c.name = 'Comedy'
    );

Résultat: le film est retenu seulement s'il est plus long que chaque film de Comedy.


Points d'attention dans les requêtes réelles

  • Pour une seule valeur, utilisez une sous-requête scalaire avec un opérateur de comparaison.
  • Pour une liste de valeurs, utilisez IN ou EXISTS selon le besoin.
  • Pour trouver des relations absentes, privilégiez NOT EXISTS, surtout si des NULL sont possibles.
  • Vérifiez toujours si la sous-requête peut retourner plus de lignes que prévu.

Points clés de cette leçon:

  • WHERE avec sous-requête permet un filtrage dynamique sans substitution manuelle de valeurs.
  • IN est pratique pour vérifier l'appartenance à une liste de valeurs.
  • EXISTS et NOT EXISTS sont efficaces pour vérifier la présence ou l'absence de lignes liées.
  • ANY et ALL permettent de comparer une ligne à un ensemble de valeurs.
  • Le bon choix d'opérateur rend les requêtes plus précises, plus lisibles et plus fiables.

Questions fréquemment posées

Que vaut-il mieux utiliser pour trouver des relations absentes: NOT IN ou NOT EXISTS?

Dans la plupart des cas pratiques, NOT EXISTS est plus sûr. Si la sous-requête de NOT IN retourne NULL, le résultat peut devenir inattendu et filtrer trop de lignes.

Pourquoi écrit-on souvent SELECT 1 dans EXISTS et non SELECT *?

Parce que EXISTS vérifie uniquement la présence de lignes. Le contenu des colonnes n'est pas utilisé, donc SELECT 1 est une forme standard et claire.

Quand utiliser ANY et quand utiliser ALL?

Utilisez ANY quand la condition doit être vraie pour au moins une valeur de la sous-requête. Utilisez ALL quand la condition doit être vraie pour chaque valeur de l'ensemble.

Questions d'entretien

Quelle est la différence entre IN et EXISTS en SQL?

IN compare une valeur à une liste retournée par une sous-requête, tandis que EXISTS vérifie la présence d'au moins une ligne correspondante. Sur de gros volumes, EXISTS est souvent plus performant dans des scénarios corrélés, car il peut s'arrêter à la première correspondance.

Comment expliquer la différence entre une sous-requête scalaire et une sous-requête multi-lignes?

Une sous-requête scalaire retourne une seule valeur et s'utilise avec des opérateurs comme = ou >. Une sous-requête multi-lignes retourne un ensemble de valeurs et s'utilise généralement avec IN, ANY ou ALL.

Pourquoi une requête avec l'opérateur = et une sous-requête peut-elle échouer?

L'opérateur = attend une seule valeur à droite. Si la sous-requête retourne plus d'une ligne, le moteur SQL ne peut pas faire une comparaison non ambiguë et retourne une erreur.

Essayez de resoudre les exercices suivants pour consolider ce que vous avez appris dans cette lecon.

  1. Obtenir la liste des sous-départements
  2. Employés avec salaires élevés
  3. Employés avec un salaire supérieur à la moyenne
  4. Manchots au petit bec
  5. Trouver des adresses en utilisant une sous-requête