L' EXCEPT
opérateur 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.
L' EXCEPT
opérateur 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.
Réponses:
Il existe deux différences clés entre EXCEPT
et NOT IN
.
EXCEPT
filtre les DISTINCT
valeurs 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 DISTINCT
clause.
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 DISTINCT
valeurs 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 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
, en filtrant les 1 et 3 qui correspondent #ExistingCustomers
et le 8 en double.
NOT IN
ne fait pas ce filtrage distinct et renvoie 4 lignes #NewCustomers
avec le duplicata 8.
Si nous ajoutons maintenant a NULL
à la #ExistingCustomers
table, nous voyons les mêmes résultats renvoyés par EXCEPT
, mais NOT IN
nous retournerons 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 cela 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, jouer NOT IN
avec plus d'une colonne.
Par exemple, il s'agit d'une requête SQL * parfaitement légale :
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 reviendra first_name
et last_name
de toutes les personnes qui sont des salariés, mais qui ne sont pas aussi des managers.
*: mais la construction n'est pas encore implémentée dans SQL Server.
Le NOT IN ci-dessus échoue car il doit y avoir une corrélation entre les prédicats de la requête principale et de la sous-requête. Si vous l'omettez, vous obtenez une sous-requête NON CORRIGÉE.
SELECT * FROM TableA AS nc WHERE ID NOT IN (SELECT ID, Name FROM TableB AS ec où nc.ID = ec.ID)
EXCEPT est meilleur et gérera toutes les lignes nulles sans utiliser les prédicats IS NULL / IS NOT NULL.