SELECT foreignStockId
FROM [Subset].[dbo].[Products]
Probably returns a NULL
.
A NOT IN
query will not return any rows if any NULL
s exists in the list of NOT IN
values. You can explicitly exclude them using IS NOT NULL
as below.
SELECT stock.IdStock,
stock.Descr
FROM [Inventory].[dbo].[Stock] stock
WHERE stock.IdStock NOT IN (SELECT foreignStockId
FROM [Subset].[dbo].[Products]
WHERE foreignStockId IS NOT NULL)
Or rewrite using NOT EXISTS
instead.
SELECT stock.idstock,
stock.descr
FROM [Inventory].[dbo].[Stock] stock
WHERE NOT EXISTS (SELECT *
FROM [Subset].[dbo].[Products] p
WHERE p.foreignstockid = stock.idstock)
As well as having the semantics that you want the execution plan for NOT EXISTS
is often simpler as looked at here.
The reason for the difference in behaviour is down to the three valued logic used in SQL. Predicates can evaluate to True
, False
, or Unknown
.
A WHERE
clause must evaluate to True
in order for the row to be returned but this is not possible with NOT IN
when NULL
is present as explained below.
'A' NOT IN ('X','Y',NULL)
is equivalent to 'A' <> 'X' AND 'A' <> 'Y' AND 'A' <> NULL)
- 'A' <> 'X' =
True
- 'A' <> 'Y' =
True
- 'A' <> NULL =
Unknown
True AND True AND Unknown
evaluates to Unknown
per the truth tables for three valued logic.
The following links have some additional discussion about performance of the various options.