为什么以下两个都返回零?当然第二个是对第一个的否定?我正在使用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是一个虚假的真值。
因此,如果您的任何参数为null,则@a = @b
的结果将为null(falsy),并且否定该参数也将为null(falsy)。
要以你想要的方式使用否定,你需要摆脱NULL。但是,简单地反转比较结果可能更容易:
case when (...) then 1 else 0 end,
case when (...) then 0 else 1 end
哪个总会给你1, 0
或0, 1
。
编辑:
正如jpmc26所指出的那样,对于nulls的行为方式进行扩展可能是有用的,这样你就不会认为单个NULL
会使NULL
变成一切。当其中一个参数为空时,有些运算符并不总是返回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 U
是U
,而not(U)
也是U
,这是假的。
这很重要,可以解释为什么当两个参数都为null时,你的表达式按预期的方式运行 - @a is null and @b is null
的计算结果为true,而true or unknown
的计算结果为true
。
您遇到的这种“怪异”行为是由NULL
值引起的。
NOT (Something that returns NULL)
的否定不是TRUE
,它仍然是NULL
。
EG
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
。你应该注意,根本不建议这样做,你应该避免它!
@ 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
NOT
总是一个否定。 T-SQL的这种行为的原因在于,null
值以特殊方式处理,具体取决于数据库配置设置(称为ansi_nulls
)。根据此设置,null
的处理方式与其他任何值相同,或者被视为“未设置值”。在这种情况下,包含空值的所有表达式都被视为无效。
此外,表达
(@a IS NULL AND @b IS NULL)
OR
@a = @b
仅涵盖两个变量都是NULL
的情况,它不处理@a
或@b
是NULL
的情况。如果发生这种情况,结果取决于ansi_nulls
的设置:如果它是on
,那么@a = @b
的结果总是false
,如果其中一个变量是NULL
。
如果ansi_nulls
是off
,那么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
它返回:
换句话说,在这个脚本中null
被视为一个值,因此a='a'
和b=null
返回0
,这是你所期望的。只有当两个变量相等(或两个null
)时,它才会返回1
。