Choisir entre EXISTS et IN en SQL est l'une des questions les plus fréquentes en entretien technique data — et pourtant, la plupart des candidats répondent de manière approximative. Ces deux opérateurs permettent de filtrer des lignes en fonction d'une sous-requête, mais leur comportement interne, leurs performances et leurs cas d'usage diffèrent radicalement. Maîtriser cette distinction, c'est démontrer une vraie compréhension du moteur de base de données, pas seulement de la syntaxe. Dans cet article, vous allez comprendre comment chaque opérateur fonctionne sous le capot, dans quels cas l'un surpasse l'autre, et comment répondre avec précision à cette question en entretien.
📌 Ce qu'il faut retenir
EXISTSvérifie l'existence d'au moins 1 ligne dans la sous-requête, et s'arrête dès qu'elle en trouve une (court-circuit).INévalue toute la liste de valeurs retournée par la sous-requête avant de filtrer.EXISTSgère mieux les valeursNULLqueINdans les sous-requêtes.- Le choix optimal dépend du volume de données, de la présence de
NULLet de la corrélation de la sous-requête.
Comment fonctionne IN en SQL
L'opérateur IN compare une valeur à une liste de valeurs retournée par une sous-requête (ou saisie en dur). SQL exécute d'abord la sous-requête dans sa totalité, charge l'ensemble des résultats en mémoire, puis filtre les lignes de la table principale une par une.
-- Trouver les clients ayant passé au moins une commande
SELECT client_id, nom
FROM clients
WHERE client_id IN (
SELECT client_id
FROM commandes
);
Le moteur évalue la sous-requête SELECT client_id FROM commandes en entier, puis vérifie pour chaque ligne de clients si son client_id figure dans cette liste. Si la table commandes contient 10 millions de lignes distinctes, SQL charge 10 millions de valeurs en mémoire avant de commencer la comparaison.
Le piège des NULL avec IN
Voici un comportement que beaucoup d'intervieweurs testent explicitement. Considérez ce cas :
-- Table produits : ids 1, 2, 3
-- Table categories : ids 1, NULL
SELECT * FROM produits
WHERE categorie_id NOT IN (
SELECT categorie_id FROM categories
);
Ce requête ne retourne aucune ligne, même si on s'attend à voir le produit avec categorie_id = 2 ou 3. En SQL, toute comparaison avec NULL retourne UNKNOWN, et NOT IN avec un NULL dans la liste donne toujours UNKNOWN — ce qui est interprété comme FALSE. Résultat : 0 lignes retournées, sans message d'erreur.
⚠️ Attention
Utiliser NOT IN avec une sous-requête pouvant retourner des NULL est un piège classique. Préférez systématiquement NOT EXISTS dans ce cas, ou filtrez explicitement les NULL avec WHERE colonne IS NOT NULL dans la sous-requête.
Comment fonctionne EXISTS en SQL
EXISTS est un opérateur semi-jointif qui teste uniquement si la sous-requête retourne au moins 1 ligne. Dès qu'une ligne correspondante est trouvée, le moteur arrête l'évaluation pour cette ligne de la table principale — c'est le principe du court-circuit (short-circuit evaluation).
-- Même objectif : clients ayant passé au moins une commande
SELECT c.client_id, c.nom
FROM clients c
WHERE EXISTS (
SELECT 1
FROM commandes co
WHERE co.client_id = c.client_id
);
Notez deux choses importantes :
- La sous-requête utilise
SELECT 1(ouSELECT *) : la valeur retournée n'a aucune importance, seule l'existence d'une ligne compte. - La sous-requête est corrélée : elle référence
c.client_idde la requête externe, ce qui signifie qu'elle est réévaluée pour chaque ligne declients.
EXISTS et les NULL : un comportement plus prévisible
EXISTS ne compare pas de valeurs — il vérifie uniquement la présence ou l'absence de lignes. Les NULL présents dans les colonnes n'affectent donc pas son résultat. NOT EXISTS retourne vrai si la sous-requête est vide, indépendamment des valeurs des colonnes.
-- NOT EXISTS : comportement fiable même avec des NULL
SELECT * FROM produits p
WHERE NOT EXISTS (
SELECT 1
FROM categories c
WHERE c.categorie_id = p.categorie_id
);
Cette requête retourne correctement tous les produits dont la catégorie n'existe pas dans la table categories, même si certains categorie_id sont NULL.
Tableau comparatif : EXISTS vs IN
| Critère | IN | EXISTS |
|---|---|---|
| Mode d'évaluation | Liste complète chargée en mémoire | Court-circuit dès la 1re ligne trouvée |
| Type de sous-requête | Souvent non corrélée | Souvent corrélée |
| Comportement avec NULL | Imprévisible avec NOT IN | Prévisible, NULL sans effet |
| Performance (grande table interne) | Dégradée si beaucoup de valeurs distinctes | Meilleure grâce au court-circuit |
| Performance (petite sous-requête) | Efficace, simple à optimiser | Surcoût possible par réévaluation |
| Lisibilité | Plus intuitive pour les débutants | Plus explicite sur l'intention |
| Utilisation avec NOT | NOT IN : dangereux avec NULL | NOT EXISTS : recommandé |
