【问题标题】:Not equal <> != operator on NULL不等于 <> != NULL 上的运算符
【发布时间】:2011-08-05 05:35:39
【问题描述】:

有人可以解释一下 SQL 中的以下行为吗?

SELECT * FROM MyTable WHERE MyColumn != NULL (0 Results)
SELECT * FROM MyTable WHERE MyColumn <> NULL (0 Results)
SELECT * FROM MyTable WHERE MyColumn IS NOT NULL (568 Results)

【问题讨论】:

    标签: sql sql-server tsql null


    【解决方案1】:

    &lt;&gt; 是标准 SQL-92; != 是它的等价物。两者都评估值,NULL 不是 - NULL 是一个占位符,表示没有值。

    这就是为什么您只能使用IS NULL/IS NOT NULL 作为此类情况的谓词。

    此行为并非特定于 SQL Server。所有符合标准的 SQL 方言都以相同的方式工作。

    注意:要比较您的值是否为空,请使用IS NOT NULL,而要与非空值比较,您使用&lt;&gt; 'YOUR_VALUE'。我不能说我的值是否等于 NULL,但我可以说我的值是 NULL 还是 NOT NULL。我可以比较我的值是否不是 NULL。

    【讨论】:

    • 实际上,我相信 &lt;&gt; 是在 92 规范中,但大多数供应商都支持 != 和/或它包含在以后的规范中,例如 99 或 03。
    • @Thomas:据我了解,Oracle 直到 ~9i 才支持!=,这带来了很多 ANSI-92 语法。我的信念是 MySQL 是相似的,从 4.x 开始支持。
    • 这似乎表明!= 可能已包含在以后的规范中作为&lt;&gt; 的替代品。不要让我接触到新的规格,所以我不能肯定。
    • WHERE MyColumn != NULLWHERE MyColumn = NULL 的结果是确定性的吗?或者换句话说,无论MyColumn在数据库中是否可以为空,是否保证总是返回0行?
    • 还应该注意,因为!= 只计算值,所以像WHERE MyColumn != 'somevalue' 这样的操作不会返回NULL 记录。
    【解决方案2】:

    NULL 没有值,因此无法使用标量值运算符进行比较。

    换句话说,任何值都不能等于(或不等于)NULL,因为 NULL 没有值。

    因此,SQL 有特殊的 IS NULL 和 IS NOT NULL 谓词来处理 NULL。

    【讨论】:

    • +1。而且,与 OP 声明相反,这不是“Microsoft SQL”。三元逻辑在 SQL 标准中定义,MS 在这一点上遵守标准。
    • 我并不是说这是微软独有的行为。我只是说我在 Microsoft SQL Server 上观察到了它。
    • 出于兴趣,是否存在这种(预期)行为有用的情况?在我看来,'a' != null 不返回值 (true/1) 是反直觉的,并且不时让我抓狂!我原以为“与没有价值相比有一些价值”总是“不相等”,但也许这就是我?!?
    • 我认为人们将 NULL 描述为 '没有价值' 很有趣。类似地,当数字 1 实际上是一个值时说它“有一个值”。但是 NULL 代表非值..
    • 作为一种手动解决方法,您通常可以SELECT * FROM MyTable WHERE coalesce(MyColumn, 'x') &lt;&gt; 'x' 分配一个常量(如果它是 NULL 值),前提是您为标记值 x 提供适当的数据类型(在本例中为字符串/字符)。这是 TSQL 语法,但 Oracle 和其他引擎具有类似的功能。
    【解决方案3】:

    请注意,此行为是默认 (ANSI) 行为。

    如果你:

     SET ANSI_NULLS OFF
    

    http://msdn.microsoft.com/en-us/library/ms188048.aspx

    你会得到不同的结果。

    SET ANSI_NULLS OFF 显然将来会消失...

    【讨论】:

    • +1 ... 还不够快。现在我什么时候可以在索引中获得“重复”的 NULL? :(
    • 您可以通过在过滤索引中添加 WHERE 子句(例如create unique index UK_MyTable on MyTable (Column) where Column is not null)在 SQL Server 索引中获取重复的 NULL:msdn.microsoft.com/en-us/library/cc280372.aspx
    • 文档中的注释:SET ANSI_NULLS 关闭时,等于 (=) 和不等于 () 比较运算符不遵循 ISO 标准。使用WHERE column_name = NULL 的SELECT 语句返回column_name 中具有空值的行。使用 WHERE column_name &lt;&gt; NULL 的 SELECT 语句返回列中具有非空值的行。此外,使用WHERE column_name &lt;&gt; XYZ_value 的 SELECT 语句返回所有非 XYZ_value 且非 NULL 的行。 恕我直言,最后一条语句似乎有点奇怪,因为它从结果中排除了空值!
    • 重要来自msdn doc 的注释:在 SQL Server [2014 年以后] 的未来版本中,ANSI_NULLS 将始终为 ON,并且任何显式设置的应用程序关闭选项将产生错误。 避免在新的开发工作中使用此功能,并计划修改当前使用此功能的应用程序。
    【解决方案4】:

    我只是没有看到 null 不能与其他值或其他 null 进行比较的功能和无缝原因,因为我们可以清楚地比较它并说它们在我们的上下文中是否相同。这很有趣。仅仅因为一些合乎逻辑的结论和一致性,我们需要不断地打扰它。它不是功能性的,让它更具功能性,让哲学家和科学家来判断它是否一致,是否具有“普遍逻辑”。 :) 有人可能会说这是因为索引或其他原因,我怀疑这些东西不能支持与值相同的空值。这就像比较两个空杯子,一个是藤玻璃,另一个是啤酒杯,我们不是比较对象的类型而是它们包含的值,就像你可以比较 int 和 varchar,null 更容易,它什么都没有,什么两个虚无有共同点,它们是相同的,我和其他编写 sql 的人显然可以比较,因为由于某些 ANSI 标准,我们通过以奇怪的方式比较它们而不断打破这种逻辑。为什么不使用计算机功能来为我们做这件事,我怀疑如果所有相关的东西都考虑到这一点,它会减慢速度。 “它不是空的,它什么都没有”,它不是苹果,它是 apfel,来吧......功能上是你的朋友,这里也有逻辑。最后,唯一重要的是功能,并且以这种方式使用空值会带来或多或少的功能和易用性。更有用吗?

    考虑这段代码:

    SELECT CASE WHEN NOT (1 = null or (1 is null and null is null)) THEN 1 ELSE 0 end
    

    有多少人知道这段代码会返回什么?不管有没有NOT,它都会返回0。对我来说,这不起作用而且令人困惑。在 c# 中,这一切都应该是,比较操作返回值,从逻辑上讲,这也会产生值,因为如果没有,就没有什么可比较的(除了。没有 :))。他们只是“说”:与 null 相比的任何内容都“返回” 0,这会产生许多变通方法和令人头疼的问题。

    这是把我带到这里的代码:

    where a != b OR (a is null and b IS not null) OR (a IS not null and b IS null)
    

    我只需要比较两个字段(在哪里)是否具有不同的值,我可以使用函数,但是...

    【讨论】:

    • 宾果游戏!答案和逻辑讨论。不为人知的错误和解决方法,因为不允许代码以合理的方式处理空值。
    • 我完全同意这种观点。如果我正在检查 32,显然 NULL 不等于 32,因此逻辑要求将空项包含在结果中。我这样做希望 SQL 能够以逻辑方式而不是以编程方式处理空值。
    • “你们当中有多少人知道这段代码会返回什么?”乍一看,我没有——而且最初的反应是一件非常好的事情。处理我们知道可能存在一些未知值的情况本质上是棘手和危险的。 SQL 现在的工作方式,对开发人员进行了培训,即在没有明确考虑到 NULL 应该发生的情况的情况下,要小心行事。这本质上是相同的想法,因为所有类型转换都是显式的。应该避免您询问的代码片段,并在出现时显示抛出红旗。
    【解决方案5】:

    我们使用

    SELECT * FROM MyTable WHERE ISNULL(MyColumn, ' ') = ' ';
    

    返回 MyColumn 为 NULL 的所有行或 MyColumn 为空字符串的所有行。对于许多“最终用户”来说,NULL 与空字符串问题是一个没有必要和混淆点的区别。

    【讨论】:

    • 这是最好的解决方法,只是在空字符串与空值比较并非毫无意义的少数情况下要小心。
    【解决方案6】:

    NULL 的唯一测试是 IS NULL 或 IS NOT NULL。测试相等性是无意义的,因为根据定义,人们不知道值是什么。

    这里有一篇维基百科文章可供阅读:

    https://en.wikipedia.org/wiki/Null_(SQL)

    【讨论】:

      【解决方案7】:

      在 SQL 中,您使用 NULL 评估/计算的任何内容都会导致 UNKNOWN

      这就是SELECT * FROM MyTable WHERE MyColumn != NULLSELECT * FROM MyTable WHERE MyColumn &lt;&gt; NULL 为您提供0 个结果的原因。

      为了检查NULL 的值,提供了isNull 函数。

      此外,您可以使用在第三个查询中使用的IS 运算符。

      希望这会有所帮助。

      【讨论】:

      • “在 SQL 中,您使用 NULL 评估/计算的任何内容都会导致 'NULL'”——不正确。您的意思是 UNKNOWN。
      • @MahendraLiya 没有提供 isNull 函数来检查 NULLS,但它是“Replaces NULL with the specified replacement value.”。您应该使用 IS NULL 或 IS NOT NULL 而不是 ISNULL ,这是另一回事。
      【解决方案8】:

      NULL 不能使用比较运算符与任何值进行比较。 NULL = NULL 为假。 Null 不是一个值。 IS 运算符专门用于处理 NULL 比较。

      【讨论】:

      • 当我有时使用null = null 时,我总是很喜欢困惑的人,而在一些临时查询中可能会使用1=0。如果他们抱怨,我把它改成null != null :)
      • "NULL = NULL 为假" 事实并非如此。 NULL = NULL 计算结果为 unknown 而不是 false。
      • @dportas 是这样,但我的意思是在条件下它不会被评估为真。
      • @VincentRamdhanie 既不为假;事实上,在 postgres 中它会被评估为 NULL
      【解决方案9】:

      老问题,但以下可能会提供更多细节。

      null 表示无值或未知值。它没有说明为什么没有值,这可能会导致一些歧义。

      假设您运行这样的查询:

      SELECT *
      FROM orders
      WHERE delivered=ordered;
      

      也就是说,您正在寻找ordereddelivered 日期相同的行。

      当一列或两列都为空时会发生什么?

      因为至少有一个日期是未知的,所以您不能指望说这两个日期相同。当两个日期都是未知的情况下也是如此:如果我们甚至不知道它们是什么,它们怎么可能相同?

      因此,任何将null 视为值的表达式都必须失败。在这种情况下,它将不匹配。如果您尝试以下操作,也会出现这种情况:

      SELECT *
      FROM orders
      WHERE delivered<>ordered;
      

      再次,如果我们不知道它们是什么,我们怎么能说两个值是相同的。

      SQL 对缺失值有特定的测试:

      IS NULL
      

      具体来说,它不是比较值,而是寻找缺失的值。

      最后,关于!= 运算符,据我所知,它实际上不在任何标准中,但得到了非常广泛的支持。添加它是为了让某些语言的程序员更有宾至如归的感觉。坦率地说,如果程序员很难记住他们使用的是什么语言,那么他们的开端就很糟糕。

      【讨论】:

      • 这与@Hove 在他的回答中描述的“荒谬”“逻辑”相同。事实是,在这种情况下,不需要额外的用具。可以很容易地假设,当我们将某物与NULL 进行比较时,我们的意思是我们将一个值与“具有NULL 值”进行比较,而不是将该值与“底层NULL 的未确定值¿有吗?但我们不知道”,显然我们永远无法知道。这真的会让事情变得轻松起来。
      • @Pere 我不会说这完全是“荒谬的”,而且我不确定写IS NULL 是否比写= NULL 更艰巨。我认为如果WHERE columnA = columnBWHERE columnA = NULL 具有相同的解释,而不是将后者视为特例,那会更加一致。请记住,NULL不是一个值。在编程语言中,测试variable == null 是合法的,这是因为null 具有不同的含义;它不代表未知的东西,而是故意重置一个值。 SQL 并非如此。
      • 这就是为什么我把它放在引号之间,@Mangoo ;)(还有“逻辑”)。不要生我的气;我说的是ANSI“推理”,而不是你的解释。我同意,在您的最新示例中,IS NULL=NULL 之间没有开销。但是看看 Hover 的最后一个。我厌倦了一次又一次地经历它,不得不做很多不必要的事情?额外检查...
      【解决方案10】:

      我想建议我制作的这段代码,以查找值是否发生变化, i 是新值,d 是旧值(尽管顺序无关紧要)。就此而言,从 value 更改为 null 或反之亦然是更改,但从 null 更改为 null 不是(当然,从 value 到另一个 value 是更改,但从 value 到相同则不是)。

      CREATE FUNCTION [dbo].[ufn_equal_with_nulls]
      (
          @i sql_variant,
          @d sql_variant
      )
      RETURNS bit
      AS
      BEGIN
          DECLARE @in bit = 0, @dn bit = 0
          if @i is null set @in = 1
          if @d is null set @dn = 1
      
          if @in <> @dn
              return 0
      
          if @in = 1 and @dn = 1
              return 1
      
          if @in = 0 and @dn = 0 and @i = @d
              return 1
      
          return 0
      
      END
      

      要使用此功能,您可以

      declare @tmp table (a int, b int)
      insert into @tmp values
      (1,1),
      (1,2),
      (1,null),
      (null,1),
      (null,null)
      
      ---- in select ----
      select *, [dbo].[ufn_equal_with_nulls](a,b) as [=] from @tmp
      
      ---- where equal ----
      select *,'equal' as [Predicate] from @tmp where  [dbo].[ufn_equal_with_nulls](a,b) = 1
      
      ---- where not equal ----
      select *,'not equal' as [Predicate] from @tmp where  [dbo].[ufn_equal_with_nulls](a,b) = 0
      

      结果是:

      ---- in select ----
      a   b   =
      1   1   1
      1   2   0
      1   NULL    0
      NULL    1   0
      NULL    NULL    1
      
      ---- where equal ----
      1   1   equal
      NULL    NULL    equal
      
      ---- where not equal ----
      1   2   not equal
      1   NULL    not equal
      NULL    1   not equal
      

      sql_variant 的使用使其兼容多种类型

      【讨论】:

        【解决方案11】:

        NULL 什么都不是……它是未知的。 NULL 不等于任何东西。这就是为什么您必须在 SQL 查询中使用魔法短语 IS NULL 而不是 = NULL

        你可以参考这个:http://weblogs.sqlteam.com/markc/archive/2009/06/08/60929.aspx

        【讨论】:

          猜你喜欢
          • 2016-08-28
          • 2010-12-19
          • 2011-05-04
          • 2012-09-03
          • 2016-07-29
          • 2014-03-22
          • 1970-01-01
          • 1970-01-01
          • 2015-10-18
          相关资源
          最近更新 更多