Groups | Blog | Home
all groups > sql server programming > october 2007 >

sql server programming : Unexpected result from NOT IN


Alex Kuznetsov
10/2/2007 1:42:17 PM
On Oct 2, 3:20 pm, "Tibor Karaszi"
[quoted text, click to view]

Hi Tibor,

with all due respect let me correct your post,

col1 in (1,Null)
means
col1 = 1 OR col1 = NULL

even though (col1 = NULL) is always unknown, the whole expression can
be true

but:

col1 NOT in (1,Null)
means
NOT(col1 = 1 OR col1 = NULL)
applying DeMorgan's law:
(col1 <> 1 AND col1 <> NULL)

and that is always unknown because (col1 = NULL) is always unknown.

Tom Cooper
10/2/2007 4:27:22 PM
Because col1 not in (<value list>) is eqivalent to a sequence of <>
comparisons of col1 to the values in the value list. That is
col1 not in (1, 5, 27)
is logically the same as
col1 <> 1 AND col1 <> 5 AND col1 <> 27

So
col1 not in (1,Null)
is the same as
col1 <> 1 AND col1 <> Null
But no matter what value is in col1, col1<>Null is neither TRUE nor FALSE,
it is "unknown" and when you AND "UNKNOWN" with something else, the rules
are:
FALSE AND UNKNOWN = FALSE
TRUE AND UNKNOWN = UNKNOWN

So when you check if 2 is not in (1,null) the result you get is UNKNOWN and
the row is returned only if the result is TRUE.

Tom

[quoted text, click to view]

Roy Harvey (SQL Server MVP)
10/2/2007 4:30:47 PM
I'd be hard pressed to explain the "logic", but that is the expected
behavior of NOT IN. Remember it and code for it is all I can suggest.

Usually it is more of a problem with a subquery than a hard coded list
of values, but those can be written as NOT EXISTS to avoid the
problem.

Roy Harvey
Beacon Falls, CT

[quoted text, click to view]
Mike C#
10/2/2007 7:13:46 PM
[quoted text, click to view]

The 3VL truth table for OR looks like this:

p | q | p OR q
---------+----------+----------
TRUE | TRUE | TRUE
TRUE | FALSE | TRUE
TRUE | UNKNOWN | TRUE
FALSE | FALSE | FALSE
FALSE | TRUE | TRUE
FALSE | UNKNOWN | UNKNOWN
UNKNOWN | TRUE | TRUE
UNKNOWN | FALSE | UNKNOWN
UNKNOWN | UNKNOWN | UNKNOWN

The NOT truth table is a little shorter:

p | NOT p
---------+----------
TRUE | FALSE
FALSE | TRUE
UNKNOWN | UNKNOWN

For (p OR q), where where either p or q is UNKNOWN, you'll always get either
TRUE or UNKNOWN as a result. For NOT (p OR q), you'll always get the
opposite: FALSE or UNKNOWN.

Mr Tea
10/2/2007 9:07:14 PM
This is the reason why some subquery NOT IN return no records when you were
expecting results. If one of your subquery rows is NULL, then the same logic
applies.

IS NOT ( x OR y ) always guaranteed to return false if one of the sides is
unknown ? In one way its good as it maintains consistency with the
interchangeable NOT( x ) AND NOT( y ) but on the other hand its bad as it
isn't the inverse of ( x OR y ) !

Mr Tea

[quoted text, click to view]

Tibor Karaszi
10/2/2007 10:20:59 PM
[quoted text, click to view]

Let's translate above:


col1 not in (1,Null)
col1 <> 1 OR col1 <> NULL

The comparison to NULL is UNKNOWN, meaning that that the whole (col1 <> 1 OR col1 <> NULL) is
UNKNOWN. And UNKNOWN in a WHERE clause is converted to FALSE [1], meaning that no rows can possibly
match that condition.

[1] UNK in WHERE is converted to false, since if you have for example:
col = 23
If any row have NULL in the col column, that row is not returned (i.e., UNK -> FALSE in WHERE
clause).
--
Tibor Karaszi, SQL Server MVP
http://www.karaszi.com/sqlserver/default.asp
http://sqlblog.com/blogs/tibor_karaszi


[quoted text, click to view]
Tibor Karaszi
10/2/2007 10:50:51 PM
Hi Alex,

[quoted text, click to view]

Note to self: Don't post when in a hurry, and when mind is already somewhere else (girlfriend was
calling me...). Yes, you are right of course.
Thanks. :-)

--
Tibor Karaszi, SQL Server MVP
http://www.karaszi.com/sqlserver/default.asp
http://sqlblog.com/blogs/tibor_karaszi


[quoted text, click to view]
Leila
10/2/2007 11:35:54 PM
Hi,
When I run this code, it returns "2" which is expected:

declare @x table(col1 int)
insert @x select 1
insert @x select 2
select * from @x where col1 not in (1)

But when NULL is added to the list of IN, then "2" disappears from te
resultset:

declare @x table(col1 int)
insert @x select 1
insert @x select 2
select * from @x where col1 not in (1,Null)

What's the logic behind this?
Thanks in advance,
Leila

Hugo Kornelis
10/5/2007 9:40:56 PM
[quoted text, click to view]

Hi Roy,

I recently took a shot at explaining the logic in my blog, in the third
part of my four-part series about NULL. You can find it at the URL
below. For a proper understanding, I encourage you to read the two
previous parts first.

First part (what NULL is, what it isn't, and why even great names such
as Codd and Date got it wrong):
http://sqlblog.com/blogs/hugo_kornelis/archive/2007/07/06/null-ndash-the-database-rsquo-s-black-hole.aspx

Second part (how NULL invalidates two-valued logic and how three-valued
logic solves this):
http://sqlblog.com/blogs/hugo_kornelis/archive/2007/07/17/the-logic-of-three-valued-logic.aspx

Third part (some curious side-effects of three-valued logic, such as how
NULL affects NOT IN, explained):
http://sqlblog.com/blogs/hugo_kornelis/archive/2007/09/22/dr-unknown-or-how-i-learned-to-stop-worrying-and-love-the-null.aspx

Fourth part (NULL related functions):
http://sqlblog.com/blogs/hugo_kornelis/archive/2007/09/30/what-if-null-if-null-is-null-null-null-is-null.aspx

--
Hugo Kornelis, SQL Server MVP
AddThis Social Bookmark Button