Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Why is empty subquery in SQL IN sometimes treated as null

I noticed the following strange behavior in SQL Server

SELECT *
FROM Tbl
WHERE col NOT IN ((SELECT 1 WHERE 1 = 2))

returns all records (i.e. the subquery result is the empty set) while

SELECT *
FROM Tbl
WHERE col NOT IN (1, (SELECT 1 WHERE 1 = 2))

return no records (i.e. the subquery result is null)

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

Got the same result in DBFiddle with postgresql and mysql. Sqlite returns no records with both queries because of the extra parenthesis.

Am I thinking completely wrong here?

Could not find anything in the documentation.

The background is I have a method to convert @Param to @Param1, @Param2, @Param3... where the number of parameters come from a sequence.

I want the method to also work for the empty sequence and in that case I replace with (SELECT null WHERE 1 = 2) but it doesn’t work when the input is NOT IN (1, @Param)

Is there some other solution to this?

Here is the fiddle code

CREATE TABLE Tbl (col int NOT NULL);
INSERT INTO Tbl VALUES (1);
INSERT INTO Tbl VALUES (2);

I expected the second query to return "2"

>Solution :

The IN syntax is

test_expression [ NOT ] IN   
    ( subquery | expression [ ,...n ]  
    )   

In your first example you are using the subquery route.

In the second example you are providing a list of expression. These are scalar values. A sub query is only accepted in this context if it can be coerced to a scalar value (returns 0 or 1 rows)

It is usual that an empty result set will return NULL when used as a scalar value.

You also see this with

SELECT 1, (SELECT 1 WHERE 1 = 2)

NOT IN(NULL) returns no rows which is the exact opposite behaviour to NOT IN (empty_set) (returns every row including NULL)

You can combine the values with UNION ALL to keep it as a sub query and prevent an empty set being coerced to NULL

SELECT *
FROM   Tbl
WHERE  col NOT IN (SELECT 1 WHERE  1 = 2
                   UNION ALL
                   SELECT 1) 
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading