SQLNOTIN 不工作

我有两个数据库,一个保存库存,另一个包含主数据库记录的子集。

下面的 SQL 语句不起作用:

SELECT  stock.IdStock
,stock.Descr
FROM    [Inventory].[dbo].[Stock] stock
WHERE   stock.IdStock NOT IN
(SELECT foreignStockId FROM
[Subset].[dbo].[Products])

不在没有工作。删除 NOT 会得到正确的结果,即两个数据库中都存在的产品。但是,使用 NOTIN 根本不会返回任何结果。

我哪里做错了,有什么想法吗?

90918 次浏览
SELECT foreignStockId
FROM   [Subset].[dbo].[Products]

Probably returns a NULL.

A NOT IN query will not return any rows if any NULLs 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.

If NOT IN does not work, you may always try to do LEFT JOIN. Then filter by WHERE using one of the values from the joined table, which are NULL. Provided, the value you were joining by does not contain any NULL value.

You can also use Case clause to tackle such issues

SELECT  stock.IdStock
,stock.Descr
FROM    [Inventory].[dbo].[Stock] stock
WHERE   (Case when stock.IdStock IN
(SELECT foreignStockId FROM
[Subset].[dbo].[Products]) then 1 else 0 end) = 0

this syntax works in SQL Server, Oracle and postgres

Adding my 2 cents:

I've seen SQL Server returning wrong results even when switching to not exists and left join - in corrupt databases. Run DBCC CHECKTABLE on the tables involved, also look at the NOT IN query execution plan and rebuild the indexes involved, this should help.