EXCEPT operator vs NOT IN
There are two key differences between EXCEPT
and NOT IN
.
EXCEPT
EXCEPT
filters the DISTINCT
values from the left-hand table that do not appear in the right-hand table. It's essentially the same as doing a NOT EXISTS
with a DISTINCT
clause.
It also expects the two tables (or subset of columns from the tables) to have the same number of columns in the left and right hand side of the query
For example, you cannot do:
SELECT ID, Name FROM TableA
EXCEPT
SELECT ID FROM TableB
This would result in the error:
All queries combined using a UNION, INTERSECT or EXCEPT operator must have an equal number of expressions in their target lists.
NOT IN
NOT IN
does not filter for DISTINCT
values and returns all values from the left-hand table that do not appear in the right-hand table.
NOT IN
requires you compare a single column from one table with a single column from another table or subquery.
For example, if your subquery was to return multiple columns:
SELECT * FROM TableA AS nc
WHERE ID NOT IN (SELECT ID, Name FROM TableB AS ec)
You'd get the following error:
Only one expression can be specified in the select list when the subquery is not introduced with EXISTS.
However, if the right-hand table contains a NULL
in the values being filtered by NOT IN
, an empty result set is returned, potentially giving unexpected results.
EXAMPLE
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)
From the above two queries, EXCEPT
returns 3 rows from #NewCustomers
, filtering out the 1 and 3 that match #ExistingCustomers
and the duplicate 8.
NOT IN
does not do this distinct filtering and returns 4 rows from #NewCustomers
with the duplicate 8.
If we now add in a NULL
to the #ExistingCustomers
table, we see the same results returned by EXCEPT
, however NOT IN
will return an empty result set.
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;
Instead of NOT IN
, you should really look at NOT EXISTS
and there is a good comparison between the two on Gail Shaw's blog .