【问题标题】:SQL And NULL Values in where clausewhere 子句中的 SQL 和 NULL 值
【发布时间】:2010-10-14 21:04:25
【问题描述】:

所以我有一个返回产品列表的简单查询

SELECT     Model, CategoryID
FROM         Products
WHERE     (Model = '010-00749-01') 

这会返回

010-00749-01    00000000-0000-0000-0000-000000000000
010-00749-01    NULL

这是正确的,所以我只想要 CategoryID 不是'00000000-0000-0000-0000-000000000000'的产品,所以我有

SELECT     Model, CategoryID
FROM         Products
WHERE     (Model = '010-00749-01') 
AND (CategoryID <> '00000000-0000-0000-0000-000000000000') 

但这不会返回任何结果。所以我将查询更改为

SELECT     Model, CategoryID
FROM         Products
WHERE     (Model = '010-00749-01') 
AND ((CategoryID <> '00000000-0000-0000-0000-000000000000') OR  (CategoryID  IS NULL))

返回预期结果

010-00749-01    NULL

有人可以向我解释这种行为吗? 微软 SQL Server 2008

【问题讨论】:

标签: sql sql-server sql-server-2008 null


【解决方案1】:

查看Books Online 上的完整参考 - 默认情况下 ANSI_NULLS 处于开启状态,这意味着您需要使用您已经完成的方法。否则,您可以在查询开始时将该设置关闭以切换行为轮次。

当 SET ANSI_NULLS 为 ON 时,SELECT 使用 WHERE column_name 的语句 = NULL 即使 ​​column_name 中有空值,也会返回零行。一种 使用 WHERE 的 SELECT 语句 column_name NULL 返回零行 即使有非空值 列名。
...
当设置 ANSI_NULLS 为 ON,所有与 null 的比较 值评估为 UNKNOWN。当设置 ANSI_NULLS 为 OFF,所有比较 针对空值的数据评估为 如果数据值为 NULL,则为 TRUE。

这里有一个简单的例子来演示与 NULL 比较的行为:

-- This will print TRUE
SET ANSI_NULLS OFF;
IF NULL <> 'A'
    PRINT 'TRUE'
ELSE
    PRINT 'FALSE'

-- This will print FALSE
SET ANSI_NULLS ON;
IF NULL <> 'A'
    PRINT 'TRUE'
ELSE
    PRINT 'FALSE'

【讨论】:

  • 小心,将 ANSI_NULLS 设置为 off 会降低性能并增加逻辑读取。开启它会影响查询计划。
  • @Vol7ron 真的值得投反对票吗?!对正确答案的要求很苛刻。如果你真的觉得它值得一票否决,足够公平,但恕我直言似乎很奇怪而不是诚实的投票
  • 什么值得反对?我不认为它值得下跌或上涨。这是一个答案。我也不会说它是“正确的”。实现它会影响性能,但更重要的是它在技术上不是 SQL 标准,当你实现它的那一刻,你就偏离了 ISO。 SQL 最好的事情之一就是能够从一个平台转移到另一个平台,你在这里放弃了它。 Coalesce 是一个更好的解决方案。正如我所说,如果您担心性能影响,请使用函数索引该字段,否则清理基础数据。
  • 警告:ANSI_NULLS 应该避免,MS 说不,它的弃用即将到来:msdn.microsoft.com/en-us/library/ms188048.aspx
【解决方案2】:

通常,您必须记住,NULL 通常表示 UNKNOWN。这意味着如果您说CategoryID &lt;&gt; '00000000-0000-0000-0000-000000000000',您必须假设查询将只返回它知道将满足您的条件的值。由于存在 NULL (UNKNOWN) 结果,它实际上并不知道该记录是否符合您的条件,因此不会在数据集中返回。

【讨论】:

  • 虽然其他答案包含更多细节,但由于简单,我喜欢这个答案。即使对于非技术人员来说也是可以理解的,因此对于新开发人员来说很有价值。 +1。
【解决方案3】:

基本上,NULL 是没有任何值。因此,尝试将 CategoryId 中的 NULL 与查询中的 varchar 值进行比较总是会导致错误的评估。

您可能想尝试使用 COALESCE 函数,例如:

SELECT     ModelId, CategoryID 
FROM       Products 
WHERE      (ModelId = '010-00749-01')  
AND        ( COALESCE( CategoryID, '' ) <> '00000000-0000-0000-0000-000000000000' ) 

编辑

AdaTheDev 所述,COALESCE 函数将否定 CategoryID 列上可能存在的任何索引,这会影响查询计划和性能。

【讨论】:

  • 谨慎使用这种方法,因为 COALESCE 可能会阻止对该列的索引搜索,从而导致不太理想的执行计划
  • Ada 是正确的,COALESCE 确实对执行计划有影响。将编辑我的答案以包括在内。
【解决方案4】:

看看这个:

1=1        --true
1=0        --false
null=null  --false
null=1     --false

1<>1       --false
1<>0       --true
null<>null --false
null<>1    --false    <<<--why you don't get the row with: AND (CategoryID <> '00000000-0000-0000-0000-000000000000') 

【讨论】:

  • 从技术上讲,SQL 使用三值逻辑系统,因此所有与 NULL 的比较都会产生 UNKNOWN 而不是 FALSE。请参阅:SQL and the Snare of Three-Valued Logic
  • 称它为falseunknown,这并不重要,因为该行不包含在结果集中,这就是问题所在。
【解决方案5】:

Null 得到特殊处理。您需要显式测试 null。见http://msdn.microsoft.com/en-us/library/ms188795.aspx

【讨论】:

    【解决方案6】:

    您可以尝试使用Coalesce 函数为具有null 的字段设置默认值:

       SELECT    Model , CategoryID
       FROM      Products
       WHERE     Model = '010-00749-01'
         AND     Coalesce(CategoryID,'') <> '00000000-0000-0000-0000-000000000000'
    

    我认为问题在于您对NULL 的理解,这基本上意味着“什么都没有”。您无法将任何事物与无事物进行比较,就像您不能将数字除以 0 一样。这只是数学/科学的规则。

    编辑: 正如 Ada 所指出的,这可能会导致索引字段不再使用索引。

    解决方案:

    • 您可以使用合并函数创建索引:例如create index ... coalesce(field)
    • 您可以添加 not null 约束以防止 NULL 出现
    • 我的一个事实上的标准是总是分配默认值并且不允许空值

    【讨论】:

    • 谨慎使用这种方法,因为 COALESCE 可能会阻止对该列的索引搜索,从而导致不太理想的执行计划
    • 这可能是真的,但是,您可以创建一个索引为coalesce(field),或者添加一个非空约束以防止空值出现。我的一个事实上的标准是始终分配默认值并且永远不允许空值。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-07-20
    • 2013-11-12
    • 1970-01-01
    • 2010-10-23
    • 2011-11-17
    • 1970-01-01
    相关资源
    最近更新 更多