2

While submitting code for my graded project I was pointed that this logic with NOT IN could fail if any Flag has NULL value. I've did my homework and testing and could not confirm this, my query below always works fine even dealing with NULL. Is my professor wrong? I read about other bad cases with NOT IN with NULL but they are bit different with NULL inside parenthesis (eg. NOT IN ('Alpha',NULL) in my case I have defined literals. I just want to make sure that current code is 100% NULL proof.

Can anybody break my theory? Or do we all agree that it's correct. I'm on most recent SQL 2016 with default setting, which I think by default has ANSI NULL OFF (if it makes any difference). I actually tested my code for ON/OFF without any difference.

I also put my logical interpretation for each case to support my case. Added: in my case I don't want any NULLs selected, only need to bring single raw `4=ID, 'X-Ray' = Flag

 DROP TABLE If exists #x
  SELECT * INTO #x FROM (
      SELECT 1 ID, 'Alpha'  Flag   UNION  SELECT 777 ID, NULL     Flag  UNION
      SELECT 3 ID, 'Bravo'  Flag   UNION  SELECT 4   ID, 'X-Ray'  Flag  
 ) a 
 
SELECT  * FROM #x   
WHERE Flag  NOT IN ('Alpha','Bravo')  
--Case#1   'Alpha' <> 'Alpha'   AND   'Alpha' <> 'Bravo'     
--                TRUE                       FALSE          ==> FALSE   (based on True AND False)
--Case#2   'X-Ray' <> 'Alpha'   AND   'X-Ray' <> 'Bravo'    
--                TRUE                       TRUE           ==> TRUE    (TRUE and TRUE)
--Case#3   NULL    <> 'Alpha'   AND   NULL    <> 'Bravo'    
--                UNknown                    UNknown        ==> UKNOWN = not TRUE   (never selected )
Mich28
  • 519
  • 3
  • 14

1 Answers1

4

The problem is with null values that are within the argument to NOT IN(). So the test case would be:

SELECT  * 
FROM #x   
WHERE Flag NOT IN ('Alpha','Bravo', NULL) 

For your sample data, this returns no rows.

Why this behaves like that is because, basically, the database translates NOT IN as:

WHERE Flag <> 'Alpha' AND Flag <> 'Bravo' AND Flag <> NULL

Flag <> NULL returns NULL, regardless of the value of Flag, so the last condition can never be true, and all rows are filtered out. One says that NOT IN is not NULL-safe.

Of course, if you are using a literal list of values that never contain NULL values, then you are fine with NOT IN. The problem often arises when subqueries come into play within NOT IN.

GMB
  • 216,147
  • 25
  • 84
  • 135
  • Thanks GMB and all, yes I got this , but for my case having in the list Literals without NULLS like `not in ('Alpha','Bravo')` should be OK. No need to do anything special (ISNULL check,etc..) – Mich28 Dec 30 '20 at 01:52
  • @Mich28: if there are no `null` values within `NOT IN()`, then you are fine. – GMB Dec 30 '20 at 11:28