过滤列表中的 NULL
为什么在子查询中过滤 NULL
不起作用?
我希望通过将 NULL
添加到允许值列表中来获得正确的结果,例如:
SELECT ERP_ServiceProcess.fiStatusNew, RMA.IdRMA
FROM ERP_ServiceProcess RIGHT OUTER JOIN
RMA ON ERP_ServiceProcess.fiRMA = RMA.IdRMA
WHERE (ERP_ServiceProcess.fiStatusNew IN (NULL, 1, 7, 8))
order by ERP_ServiceProcess.fiStatusNew
这给出了错误的结果,因为 RMA 中的所有记录在子表 ERP_ServiceProcess(其中 ERP_ServiceProcess. fiStatusNew IS NULL
) 被删除。
我必须使用这个(慢速)查询才能获得正确的结果:
SELECT ERP_ServiceProcess.fiStatusNew, RMA.IdRMA
FROM ERP_ServiceProcess RIGHT OUTER JOIN
RMA ON ERP_ServiceProcess.fiRMA = RMA.IdRMA
WHERE (ERP_ServiceProcess.fiStatusNew IS NULL)
OR (ERP_ServiceProcess.fiStatusNew IN (1, 7, 8))
order by ERP_ServiceProcess.fiStatusNew
为什么我必须使用第二个慢速查询,尽管我使用了RIGHT OUTER JOIN
并且我添加了NULL
到子查询?
先感谢您。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
它无法按您的预期工作,因为它扩展为一堆等于操作
fiStatusNew = NULL OR fiStatusNew = 1 OR fiStatusNew = 7 OR fiStatusNew = 8
和
anything = NULL
未知。鉴于这种扩展,没有特别的理由认为使用
IS NULL
添加额外的OR
本身会使事情变慢(额外的谓词可能会更改查询计划以使用不同的访问路径(如果统计数据表明匹配行的数量证明了这一点)您在
CASE
操作中看到相同的行为这是您应该特别注意逆操作的原因之一
不在
。如果列表包含任何NULL
值,您将始终得到一个空结果集。fiStatusNew NOT IN (NULL, 1,2)
将扩展为
fiStatusNew<> NULL 和 fiStatusNew<> 1 和 fiStatusNew<> 2
或
Unknown And True/False/Unknown And True/False/Unknown
在 Unknown simple-talk.com/sql/learn-sql-server/sql-and-the-snare-of- Three-valued-logic/" rel="nofollow">三值逻辑。
It doesn't work as you expect as it gets expanded to a bunch of equals operations
fiStatusNew = NULL OR fiStatusNew = 1 OR fiStatusNew = 7 OR fiStatusNew = 8
and
anything = NULL
is unknown.Given this expansion there's no particular reason to think that adding an additional
OR
usingIS NULL
would make things slower on its own (the additional predicate might change the query plan to use a different access path if the statistics lead it to belive that the number of matching rows warrants this though)You see the same behaviour in the
CASE
operationThis is one reason why you should take particular care with the inverse operation
NOT IN
. If the list contains anyNULL
values you will always get an empty result set.fiStatusNew NOT IN (NULL, 1,2)
Would expand to
fiStatusNew<> NULL and fiStatusNew<> 1 and fiStatusNew<> 2
or
Unknown And True/False/Unknown And True/False/Unknown
Which always evaluates to
Unknown
under three valued logic.您可以尝试使用
未经测试的但可能比第二个查询更快。
Could you try using
Untested but might be quicker than the 2nd query.
“ERP_ServiceProcess.fiStatusNew IN (NULL)”的计算结果为“ERP_ServiceProcess.fiStatusNew = NULL”,并且始终为 false。 NULL 在 SQL Server 中被定义为“未知”,而不是“无值”。这就是为什么 NULL = NULL 或 NULL = @var (*) 总是评估为 false。如果有两个未知数,则无法检查它们是否相等。只有“is NULL”有效。
(*) 那么,对于 sql server,您可以将 ANSI_NULLS 设置为关闭但这并不是真正推荐的,因为它不是标准的 sql 行为。
'ERP_ServiceProcess.fiStatusNew IN (NULL)' evaluates to 'ERP_ServiceProcess.fiStatusNew = NULL' and that always is false. NULL is defined in sql server as 'unknown', not as 'no value'. That's why NULL = NULL or NULL = @var (*) always evaluates to false. If you have two unknowns, you cannot check if they are equal. Only 'is NULL' works.
(*) Well, for sql server, you can set ANSI_NULLS to off but that's not really recommended as it is not standard sql behaviour.