什么时候“不”不是否定?
为什么下面的两个返回零? 第二个肯定是第一个的否定? 我正在使用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所指出的那样,扩展一些空值的行为可能是有用的,这样你就不会觉得单个NULL
会使所有的东西都为 NULL
。 有些运算符当它们的一个参数为null
时,并不总是返回null
值 – 当然,最明显的例子is null
的。
在一个更广泛的例子中,T-SQL中的逻辑运算符使用Kleene的代数(或类似的东西),它定义了OR
expression式的真值,如下所示:
| T | U | F T | T | T | T U | T | U | U F | T | U | F
( AND
其他操作符一样AND
也是类似的)
所以你可以看到,如果至less有一个参数是真的,结果也是正确的,即使另一个是未知的(“null”)。 这也意味着not(T or U)
会给你一个虚假的真值,而not(F or U)
也会给你一个虚假的真值,尽pipeF or U
是虚的 – 因为F or U
是U
,而not(U)
也是U
,这是虚的。
这很重要,可以解释为什么当您的expression式按照您期望的方式工作时,两个参数都为空 – @a is null and @b is null
计算结果为true, true or unknown
计算结果为true
。
您遇到的这种“奇怪”行为是由NULL
值引起的。
NOT (Something that returns 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
视为正常值,并返回TRUE\FALSE
。 你应该注意到,这是不build议的,你应该避免它!
这是@ 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的这种行为的原因在于,根据数据库configuration设置(称为ansi_nulls
),以特殊方式处理null
值。 根据这个设置, null
或者以与其他值相同的方式处理,或者被视为“未设置值”。 在这种情况下,所有包含空值的expression式都被视为无效。
此外,expression
(@a IS NULL AND @b IS NULL) OR @a = @b
仅涵盖两个variables都为NULL
,它不处理@a
或@b
为NULL
。 如果发生这种情况,结果取决于ansi_nulls
的设置:如果它是on
,那么如果其中一个variables是NULL
,则@a = @b
的结果总是为false
。
如果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
按照它们出现的顺序处理),并且如果条件匹配,则处理结束指定的值被返回)。
为了testing所有可能的(相关的)组合,你可以使用这个脚本:
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
,这正是您所期望的。 只有两个variables相等(或两者都为null
),它才返回1
。