什么时候“不”不是否定?

时间:2016-09-13 13:23:21

标签: sql-server tsql null expression evaluation

为什么以下两个都返回零?当然第二个是对第一个的否定?我正在使用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

4 个答案:

答案 0 :(得分:50)

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

因此,如果您的任何参数为null,则@a = @b的结果将为null(falsy),并且对此的否定也将为null(falsy)。

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

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

始终会为您提供1, 00, 1

修改

正如jpmc26所指出的那样,对于空值的行为方式进行扩展可能会有所帮助,这样您就不会认为单个NULL会使一切 {{1} }。当其中一个参数为null时,有些运算符并不总是返回NULL - 当然,最明显的例子是null

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

OR

| T | U | F T | T | T | T U | T | U | U F | T | U | F 与其他运算符类似)

所以你可以看到,如果至少有一个参数为真,那么结果也是真的,即使另一个是未知的(“null”)。这也意味着AND会给你一个虚假的真值,而not(T or U)给你一个虚假的真值,尽管not(F or U)是假的 - 从{ {1}}为F or UF or U也为U,这是假的。

这很重要,可以解释为什么当两个参数都为空时,表达式的工作方式符合预期 - not(U)计算结果为true,U计算结果为@a is null and @b is null

答案 1 :(得分:8)

您遇到的这种“奇怪”行为是由NULL值引起的。

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

E.G。

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

除了这里所说的,你可以使用

来避免这种行为
SET ANSI_NULLS OFF

这将让优化器将NULL视为正常值,并返回TRUE\FALSE。你应该注意,根本不建议这样做,你应该避免使用它!

答案 2 :(得分:4)

@ 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

答案 3 :(得分:0)

NOT 总是否定。 T-SQL的这种行为的原因在于,null值以特殊方式处理,具体取决于数据库配置设置(称为ansi_nulls)。根据此设置,null的处理方式与其他任何值相同,或者被视为&#34;值未设置&#34;。在这种情况下,包含空值的所有表达式都被视为无效。

此外,表达

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

仅涵盖两个变量均为NULL时的情况,它不处理@a@bNULL时的情况。如果发生这种情况,结果取决于ansi_nulls的设置:如果是on,则@a = @b的结果始终为false,如果其中一个变量为NULL 1}}。

如果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&#39 ; s按照它们出现的顺序处理,如果条件匹配,则处理结束并返回指定的值。

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

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 

它返回:

result of query

换句话说,在此脚本中,null被视为一个值,因此a='a'b=null会返回0,这是您所期望的。只有当两个变量都相等(或两个null)时,它才会返回1