我有两个数据库,一个包含清单,另一个包含主数据库记录的子集。
以下SQL语句无效:
SELECT stock.IdStock
,stock.Descr
FROM [Inventory].[dbo].[Stock] stock
WHERE stock.IdStock NOT IN
(SELECT foreignStockId FROM
[Subset].[dbo].[Products])
不是不起作用。删除NOT会得到正确的结果,即两个数据库中的产品。但是,使用NOT IN并不会返回任何结果。
我做错了什么,有什么想法?
答案 0 :(得分:153)
SELECT foreignStockId
FROM [Subset].[dbo].[Products]
可能会返回NULL
。
如果NOT IN
值列表中存在任何NULL
,NOT IN
查询将不会返回任何行。您可以使用IS NOT NULL
明确排除它们,如下所示。
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)
或者使用NOT EXISTS
重写。
SELECT stock.idstock,
stock.descr
FROM [Inventory].[dbo].[Stock] stock
WHERE NOT EXISTS (SELECT *
FROM [Subset].[dbo].[Products] p
WHERE p.foreignstockid = stock.idstock)
除了拥有您想要的语义之外,NOT EXISTS
的执行计划通常更简单as looked at here。
行为差异的原因是SQL中使用的three valued logic。谓词可以评估为True
,False
或Unknown
。
WHERE
子句必须评估为True
才能返回行,但NOT IN
出现NULL
时无法执行此操作,如下所述。< / p>
'A' NOT IN ('X','Y',NULL)
相当于'A' <> 'X' AND 'A' <> 'Y' AND 'A' <> NULL)
True
True
Unknown
True AND True AND Unknown
根据truth tables for three valued logic评估为Unknown
。
以下链接对各种选项的表现进行了一些额外的讨论。
答案 1 :(得分:2)
如果 NOT IN 不起作用,您可以尝试 LEFT JOIN 。然后使用连接表中的一个值 NULL 按 WHERE 进行过滤。但是,加入的值不包含任何 NULL 值。
答案 2 :(得分:0)
您还可以使用Case子句来解决此类问题
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
此语法适用于SQL Server,Oracle和postgres
答案 3 :(得分:0)
加上我的2美分:
即使在损坏的数据库中切换到not exists
和left join
-,我也看到SQL Server返回错误的结果。在所涉及的表上运行DBCC CHECKTABLE,还要查看NOT IN
查询执行计划并重建所涉及的索引,这将有所帮助。