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

为什么下面的两个返回零? 第二个肯定是第一个的否定? 我正在使用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的代数(或类似的东西),它定义了ORexpression式的真值,如下所示:

  | 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 UU ,而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@bNULL 。 如果发生这种情况,结果取决于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