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.
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
INouEXISTSselon le besoin. - Pour trouver des relations absentes, privilégiez
NOT EXISTS, surtout si desNULLsont possibles. - Vérifiez toujours si la sous-requête peut retourner plus de lignes que prévu.
Points clés de cette leçon:
WHEREavec sous-requête permet un filtrage dynamique sans substitution manuelle de valeurs.INest pratique pour vérifier l'appartenance à une liste de valeurs.EXISTSetNOT EXISTSsont efficaces pour vérifier la présence ou l'absence de lignes liées.ANYetALLpermettent 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.