One of the most common mistakes made in T-SQL is thinking that these behave identically. I've personally opened up a forum topic on it because I didn't know what the difference was. This post will join a small army of other places on the net devoted to correcting this misunderstanding.
They aren't completely dissimilar; they behave exactly as you would expect them to... with the exception of NULL's. Because nothing EQUALS NULL (Dependent upon settings, see below) the difference in the internal logic matters. Gail Shaw initially explained this to me when I asked the question on the forums and I wanted to use her explanation here, but I can’t seem to find it; so here's my own version of an explanation:
When you use IN, you're really saying "WHERE myvalue = 'A' OR myvalue = 'B' OR myvalue = NULL"
Your NULLS won't cause the entire statement to fail because it's only an OR.
When you use NOT IN you're really saying 'WHERE myvalue <> 'A' AND myvalue <> 'B' AND myvalue <> NULL “
This is where the problem arises. Since a NULL in SQL is an unknown value, you can't test = or <> on it and you get no results. Without the NULL, you'd be fine.
Here's a simple example to demonstrate.
DECLARE @T TABLE( Val varchar(5)) DECLARE @T2 TABLE( Val varchar(5)) INSERT INTO @T(Val) SELECT 'A' UNION ALL SELECT 'B' UNION ALL SELECT 'C' UNION ALL SELECT 'D' UNION ALL SELECT 'E' UNION ALL SELECT 'F' INSERT INTO @T2(Val) SELECT 'A' UNION ALL SELECT 'B' UNION ALL SELECT 'C' UNION ALL SELECT NULL SET ANSI_NULLS ON SELECT * FROM @T WHERE Val IN (SELECT * FROM @T2) SELECT * FROM @T WHERE Val NOT IN (SELECT * FROM @T2) SELECT * FROM @T WHERE Val NOT IN (SELECT * FROM @T2 WHERE Val IS NOT NULL)
This issue is further complicated by the ANSI_NULLS setting. While I believe most people have this turned ON, the fact that it is an option introduces another variable into the mix. NOT IN will not fail in the same way if you have ANSI_NULLS set to OFF. (Try the above example again after changing ON to OFF)
[Edit] Related Article by Gail Shaw (whom I mentioned earlier in this post): http://sqlinthewild.co.za/index.php/2010/02/18/not-exists-vs-not-in/ [/Edit]