"NOT" 什么时候不是否定?

When is "NOT" not a negation?

为什么以下两个 return 都为零?第二个肯定是第一个的否定吗?我正在使用 SQL Server 2008。

DECLARE 
    @a VARCHAR(10) = NULL ,
    @b VARCHAR(10) = 'a'

SELECT  
    CASE WHEN ( ( @a IS NULL
                      AND @b IS NULL
                    )
                    OR @a = @b
                  ) THEN 1
         ELSE 0
    END , -- Returns 0
    CASE WHEN NOT ( ( @a IS NULL
                      AND @b IS NULL
                    )
                    OR @a = @b
                  ) THEN 1
         ELSE 0
    END -- Also returns 0

这是一个否定。但是,您需要了解 ANSI NULL——NULL 的否定也是 NULL。而 NULL 是一个虚假的真值。

因此,如果您的任何参数为空,@a = @b 的结果将为空(假),并且其否定也将为空(假)。

要以您想要的方式使用否定,您需要去掉 NULL。但是,简单地反转比较结果可能更容易:

case when (...) then 1 else 0 end,
case when (...) then 0 else 1 end

它总是会给你 1, 00, 1

编辑:

正如 jpmc26 指出的那样,扩展一下空值的行为方式可能会有用,这样您就不会认为单个 NULL 会使 一切 NULL。有些运算符在其参数之一为空时并不总是 return null - 当然,最明显的例子是 is null

在更广泛的示例中,T-SQL 中的逻辑运算符使用 Kleene 代数(或类似的东西),它定义了 OR 表达式的真值,如下所示:

  | T | U | F
T | T | T | T
U | T | U | U
F | T | U | F

AND 与其他运算符类似)

因此您可以看到,如果至少有一个参数为真,则结果也将为真,即使另一个参数未知 ("null")。这也意味着 not(T or U) 会给你一个虚假的真值,而 not(F or U) 也会 给你一个虚假的真值,尽管 F or U 是虚假的- 因为 F or UU,并且 not(U) 也是 U,这是错误的。

这很重要,可以解释为什么当两个参数都为空时您的表达式会按照您期望的方式工作 - @a is null and @b is null 计算结果为真,true or unknown 计算结果为 true

@a=@b 是有问题的,如果这个值中的任何一个为 null 就会有问题

如果您尝试下面的代码将给出正确的结果

DECLARE 
    @a VARCHAR(10) = NULL ,
    @b VARCHAR(10) = 'a'

SELECT  
    CASE WHEN ( ( @a IS NULL
                      AND @b IS NULL
                    )
                    OR @a = @b
                  ) THEN 1
         ELSE 0
    END , -- returns 0
    CASE WHEN NOT ( ( @a IS NULL
                      AND @b IS NULL
                    )
                    OR ISNULL(@a,-1) = ISNULL(@b,-1)
                  ) THEN 1
         ELSE 0
    END -- also returns 0

您遇到的这种 'weird' 行为是由 NULL 值引起的。

NOT (Something that returns NULL) 的否定不是 TRUE ,它仍然是 NULL .

例如

SELECT * FROM <Table> WHERE <Column> = null -- 0 rows 
SELECT * FROM <Table> WHERE NOT (<Column> = null) -- Still 0 rows

除了这里所说的之外,您还可以使用

来避免这种行为
SET ANSI_NULLS OFF

这将使优化器将 NULL 视为正常值,而 return TRUE\FALSE 。您应该注意,根本不推荐这样做,您应该避免这样做!

NOT 总是 一个否定。 T-SQL 这种行为的原因在于 null 值根据数据库配置设置(称为 ansi_nulls)以特殊方式处理。根据此设置,null 要么以与任何其他值相同的方式处理,要么被视为 "value not set"。在这种情况下,所有包含空值的表达式都被视为无效。

此外,表达式

(@a IS NULL AND @b IS NULL)
OR 
@a = @b

仅涵盖两个变量都为NULL的情况,它不处理@a@b为[的情况=17=]。如果发生这种情况,结果取决于 ansi_nulls 的设置:如果它是 on,那么 @a = @b 的结果总是 false 如果其中一个变量是 NULL

如果 ansi_nullsoff,则 NULL 被视为一个值并按您预期的方式运行。

为避免此类意外行为,您应该涵盖以下所有情况:

DECLARE 
    @a VARCHAR(10) = 'a',
    @b VARCHAR(10) = null

SELECT  
    CASE 
        WHEN (@a IS NOT null AND @b IS null) THEN 0
        WHEN (@a IS null AND @b IS NOT null) THEN 0
        WHEN (@a IS null AND @b IS null) THEN 1
        WHEN (@a=@b) THEN 1
    ELSE 0
    END 

注意,在这个例子中,所有空情况都在检查 @a=@b 情况之前处理(在 CASE 语句中,WHEN的按出现的顺序处理,如果条件匹配,则处理完成并返回指定的值)。


要测试所有可能的(相关)组合,您可以使用此脚本:

DECLARE @combinations TABLE (
    a VARCHAR(10),b VARCHAR(10)
    )

INSERT INTO @combinations
    SELECT 'a', null 
    UNION SELECT null, 'b'
    UNION SELECT 'a', 'b'
    UNION SELECT null, null
    UNION SELECT 'a', 'a'

SELECT a, b,
    CASE 
        WHEN (a IS NOT null AND b IS null) THEN 0
        WHEN (a IS null AND b IS NOT null) THEN 0
        WHEN (a IS null AND b IS null) THEN 1
        WHEN (a=b) THEN 1
    ELSE 0
    END as result
from @combinations
order by result 

它returns:

换句话说,在这个脚本中 null 被视为一个值,因此 a='a'b=null returns 0,这就是你预料之中。只有当两个变量相等(或都null)时,它returns 1.