L'opérateur EXCEPT
a été introduit dans SQL Server 2005 mais quelle est la différence entre NOT IN
et EXCEPT
?
Fait-il la même chose? Je voudrais une explication simple avec un exemple.
Il existe deux différences clés entre EXCEPT
et NOT IN
.
EXCEPT
filtre les valeurs DISTINCT
du tableau de gauche qui n'apparaissent pas dans le tableau de droite. C'est essentiellement la même chose que de faire un NOT EXISTS
avec une clause DISTINCT
.
Il s'attend également à ce que les deux tables (ou sous-ensemble de colonnes des tables) aient le même nombre de colonnes à gauche et à droite de la requête
Par exemple, vous ne pouvez pas faire:
SELECT ID, Name FROM TableA
EXCEPT
SELECT ID FROM TableB
Cela entraînerait l'erreur:
Toutes les requêtes combinées à l'aide d'un opérateur UNION, INTERSECT ou EXCEPT doivent avoir un nombre égal d'expressions dans leurs listes cibles.
NOT IN
ne filtre pas les valeurs DISTINCT
et renvoie toutes les valeurs du tableau de gauche qui n'apparaissent pas dans le tableau de droite.
NOT IN
vous oblige à comparer une seule colonne d'une table avec une seule colonne d'une autre table ou sous-requête.
Par exemple, si votre sous-requête devait renvoyer plusieurs colonnes:
SELECT * FROM TableA AS nc
WHERE ID NOT IN (SELECT ID, Name FROM TableB AS ec)
Vous obtiendrez l'erreur suivante:
Une seule expression peut être spécifiée dans la liste de sélection lorsque la sous-requête n'est pas introduite avec EXISTS.
Cependant, si le tableau de droite contient un NULL
dans les valeurs filtrées par NOT IN
, un jeu de résultats vide est renvoyé, ce qui peut donner des résultats inattendus.
CREATE TABLE #NewCustomers (ID INT);
CREATE TABLE #ExistingCustomers (ID INT);
INSERT INTO #NewCustomers
( ID )
VALUES
(8), (9), (10), (1), (3), (8);
INSERT INTO #ExistingCustomers
( ID )
VALUES
( 1) , (2), (3), (4), (5);
-- EXCEPT filters for DISTINCT values
SELECT * FROM #NewCustomers AS nc
EXCEPT
SELECT * FROM #ExistingCustomers AS ec
-- NOT IN returns all values without filtering
SELECT * FROM #NewCustomers AS nc
WHERE ID NOT IN (SELECT ID FROM #ExistingCustomers AS ec)
À partir des deux requêtes ci-dessus, EXCEPT
renvoie 3 lignes de #NewCustomers
, filtrant les 1 et 3 correspondant à #ExistingCustomers
et le doublon 8.
NOT IN
ne fait pas ce filtrage distinct et renvoie 4 lignes à partir de #NewCustomers
avec le doublon 8.
Si nous ajoutons maintenant un NULL
au #ExistingCustomers
table, nous voyons les mêmes résultats retournés par EXCEPT
, cependant NOT IN
renverra un jeu de résultats vide.
INSERT INTO #ExistingCustomers
( ID )
VALUES
( NULL );
-- With NULL values in the right-hand table, EXCEPT still returns the same results as above
SELECT * FROM #NewCustomers AS nc
EXCEPT
SELECT * FROM #ExistingCustomers AS ec
-- NOT IN now returns no results
SELECT * FROM #NewCustomers AS nc
WHERE ID NOT IN (SELECT ID FROM #ExistingCustomers AS ec)
DROP TABLE #NewCustomers;
DROP TABLE #ExistingCustomers;
Au lieu de NOT IN
, vous devriez vraiment regarder NOT EXISTS
et il y a une bonne comparaison entre les deux sur le blog de Gail Shaw .
Un ajout à l'excellent commentaire de Mark Sinkinson:
NOT IN vous oblige à comparer une seule colonne d'une table avec une seule colonne d'une autre table ou sous-requête.
Vous pouvez, en fait, effectuer NOT IN
avec plusieurs colonnes.
Par exemple. ceci est parfaitement légal* Requête SQL:
SELECT E.first_name, E.last_name
FROM employees E
WHERE (E.first_name, E.last_name) NOT IN
(SELECT M.first_name, M.last_name FROM managers M)
Qui renverra first_name
et last_name
de toutes les personnes qui sont des employés, mais qui ne sont pas également des managers.
*: mais la construction n'est pas encore implémentée dans SQL Server.