SQL EXISTS : filtrer avec des lignes liées

SQL EXISTS renvoie vrai quand une sous-requête corrélée trouve au moins une ligne correspondante. Utilisez-le pour filtrer des lignes parentes selon la présence (ou l’absence) de lignes enfants liées, sans dupliquer les résultats comme peut le faire un JOIN. Les optimiseurs exécutent souvent EXISTS comme un semi-join, donc ils peuvent arrêter le travail de sous-requête après la première correspondance.

Exemple SQL EXISTS pour filtrer l’existence de lignes

Sortie :

La sortie apparaîtra ici...

Sortie :

+------+
| nom  |
+------+
| Alma |
| Cara |
+------+
2 row(s)

Pourquoi EXISTS renvoie Alma et Cara

  1. Deux tables sont créées : clients contient trois personnes, commandes contient trois commandes liées par client_id.
  2. WHERE EXISTS (SELECT 1 FROM commandes o WHERE o.client_id = c.id) exécute la sous-requête une fois par ligne client. Si au moins une commande correspondante existe, le client passe le filtre.
  3. SELECT 1 dans la sous-requête est une convention ; la base ignore la liste de sélection et vérifie seulement si une ligne est qualifiée.
  4. Ben n’a aucune commande, donc sa sous-requête renvoie zéro ligne et EXISTS vaut faux. Alma et Cara ont chacune au moins une commande et apparaissent dans le résultat.

Contrairement à un INNER JOIN, EXISTS ne duplique jamais la ligne externe. Alma a deux commandes mais apparaît une seule fois, car EXISTS pose seulement la question “une correspondance existe-t-elle ?” et non “combien y en a-t-il ?”.

NOT EXISTS vs NOT IN : le piège de NULL

SQL NOT EXISTS (écrit NOT EXISTS) est le choix sûr pour les anti-joins sur des colonnes acceptant NULL. Quand une sous-requête renvoie des valeurs NULL, NOT IN produit des résultats inattendus, car toute comparaison avec NULL renvoie inconnu :

-- Supposons que commandes contienne une ligne avec client_id NULL
-- NOT IN exclut silencieusement TOUTES les lignes
SELECT nom FROM clients
WHERE id NOT IN (SELECT client_id FROM commandes);

-- NOT EXISTS fonctionne encore correctement
SELECT nom FROM clients c
WHERE NOT EXISTS (
  SELECT 1 FROM commandes o WHERE o.client_id = c.id
);

NOT IN traite id NOT IN (1, 3, NULL) comme id <> 1 AND id <> 3 AND id <> NULL. La dernière comparaison est toujours inconnue, donc le prédicat complet n’est jamais vrai et zéro ligne est renvoyée. NOT EXISTS évite ce problème, car il vérifie l’existence de lignes et non l’égalité de valeurs.

Erreurs courantes avec SQL EXISTS

Prédicat de corrélation manquant

Si la sous-requête n’est pas corrélée à la ligne externe, EXISTS devient un test constant : dès qu’une commande existe, chaque client correspond.

-- Faux : pas corrélé à la ligne externe
SELECT c.nom FROM clients c
WHERE EXISTS (SELECT 1 FROM commandes);

-- Correct : corrélé sur client_id
SELECT c.nom FROM clients c
WHERE EXISTS (SELECT 1 FROM commandes o WHERE o.client_id = c.id);

Utiliser JOIN alors que seule l’existence compte

Un INNER JOIN renvoie une ligne par correspondance, donc les clients avec plusieurs commandes apparaissent plusieurs fois.

SELECT c.nom
FROM clients c
JOIN commandes o ON o.client_id = c.id;

Utilisez EXISTS sauf si vous avez besoin de colonnes de commandes.

EXISTS vs IN vs JOIN

ApprocheUtilisez-la quand…
EXISTS (sous-requête)Vérifier l’existence de lignes liées ; la sous-requête est corrélée
NOT EXISTS (sous-requête)Anti-join, surtout avec des clés étrangères acceptant NULL
IN (sous-requête)Correspondre à un petit ensemble de valeurs sans NULL
INNER JOINVous avez besoin de colonnes des deux tables dans le résultat
LEFT JOIN ... IS NULLAlternative d’anti-join ; parfois préférée par l’optimiseur

Règle : utilisez EXISTS pour les questions “une ligne liée existe-t-elle ?”. Passez à JOIN quand vous avez besoin de données des deux côtés.

Notes de performance

EXISTS est souvent planifié comme un semi-join. Un index sur la colonne de corrélation (commandes.client_id ici) permet au moteur de trouver la première ligne correspondante par client au lieu de parcourir toute la table commandes. NOT EXISTS profite du même index. Si EXISTS est lent, vérifiez le plan d’exécution et l’indexation de la colonne de corrélation.

FAQ

EXISTS s’arrête-t-il après la première correspondance ?

Logiquement, le résultat devient vrai dès qu’une ligne qualifiée existe. Beaucoup de bases implémentent cela comme un semi-join avec arrêt à la première correspondance, mais la stratégie exacte dépend de l’optimiseur. Regardez le plan d’exécution si vous devez confirmer le comportement de votre base.

Peut-on utiliser EXISTS dans UPDATE et DELETE ?

Oui. UPDATE ... WHERE EXISTS (subquery) et DELETE ... WHERE EXISTS (subquery) limitent les lignes modifiées ou supprimées selon l’existence de lignes liées. Gardez le même prédicat de corrélation que dans un SELECT, afin que chaque ligne cible vérifie seulement ses lignes liées.

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

EXISTS vérifie qu’une sous-requête (souvent corrélée) renvoie au moins une ligne. IN compare une valeur à un ensemble de résultats. EXISTS fonctionne naturellement avec la corrélation de la requête externe, et beaucoup de moteurs peuvent s’arrêter après la première correspondance ; IN est adapté aux petites listes littérales ou aux tests d’appartenance non corrélés. Pour les anti-joins sur des colonnes acceptant NULL, préférez NOT EXISTS à NOT IN.