如何比较可能都为null的值是T-SQL

我想确保我没有插入重复行到我的表(例如只有主键不同)。 我所有的领域都允许NULLS,因为我已经决定为“所有的价值”。 由于空值,我的存储过程中的以下语句无法工作:

IF EXISTS(SELECT * FROM MY_TABLE WHERE MY_FIELD1 = @IN_MY_FIELD1 AND MY_FIELD2 = @IN_MY_FIELD2 AND MY_FIELD3 = @IN_MY_FIELD3 AND MY_FIELD4 = @IN_MY_FIELD4 AND MY_FIELD5 = @IN_MY_FIELD5 AND MY_FIELD6 = @IN_MY_FIELD6) BEGIN goto on_duplicate END 

因为NULL = NULL是不正确的。

我怎样才能检查重复没有每个列IF IF NULL语句?

使用INTERSECT运算符。

如果您在所有字段上都有复合索引,则它是NULL敏感且高效的:

 IF EXISTS ( SELECT MY_FIELD1, MY_FIELD2, MY_FIELD3, MY_FIELD4, MY_FIELD5, MY_FIELD6 FROM MY_TABLE INTERSECT SELECT @IN_MY_FIELD1, @IN_MY_FIELD2, @IN_MY_FIELD3, @IN_MY_FIELD4, @IN_MY_FIELD5, @IN_MY_FIELD6 ) BEGIN goto on_duplicate END 

请注意,如果你在你的领域创build一个UNIQUE索引,你的生活会更简单。

沿着@埃里克的答案 ,但没有使用'NULL'符号相同的线。

 (Field1 = Field2) OR (ISNULL(Field1, Field2) IS NULL) 

只有当这两个值都是non-NULL ,并且彼此相等,或者这两个值都是NULL ,这才是真的

使用ISNULL

 ISNULL(MY_FIELD1, 'NULL') = ISNULL(@IN_MY_FIELD1, 'NULL') 

如果更有意义,可以将'NULL'更改为'All Values'

需要注意的是,有两个参数, ISNULLCOALESCE工作方式相同,如果您有几个testing值(即COALESCE(@IN_MY_FIELD1, @OtherVal, 'NULL') ),您可以使用它。 在第一个非空之后, COALESCE也会返回,这意味着如果您期待MY_FIELD1为空白,则它会(稍微)更快。 但是,我发现ISNULL更具可读性,所以这就是我在这里使用它的原因。

在进行MERGE时,我需要进行类似的比较:

 WHEN MATCHED AND (Target.Field1 <> Source.Field1 OR ...) 

额外的检查是为了避免更新所有列已经相同的行。 为了我的目的,我希望NULL <> anyValue为True,而NULL <> NULL为False。

解决scheme演变如下:

第一次尝试:

 WHEN MATCHED AND ( ( -- Neither is null, values are not equal Target.Field1 IS NOT NULL AND Source.Field1 IS NOT NULL AND Target.Field1 <> Source.Field1 ) OR ( -- Target is null but source is not Target.Field1 IS NULL AND Source.Field1 IS NOT NULL ) OR ( -- Source is null but target is not Target.Field1 IS NOT NULL AND Source.Field1 IS NULL ) -- OR ... Repeat for other columns ) 

第二次尝试:

 WHEN MATCHED AND ( -- Neither is null, values are not equal NOT (Target.Field1 IS NULL OR Source.Field1 IS NULL) AND Target.Field1 <> Source.Field1 -- Source xor target is null OR (Target.Field1 IS NULL OR Source.Field1 IS NULL) AND NOT (Target.Field1 IS NULL AND Source.Field1 IS NULL) -- OR ... Repeat for other columns ) 

第三次尝试(由THEn的答案启发):

 WHEN MATCHED AND ( ISNULL( NULLIF(Target.Field1, Source.Field1), NULLIF(Source.Field1, Target.Field1) ) IS NOT NULL -- OR ... Repeat for other columns ) 

相同的ISNULL / NULLIF逻辑可用于testing相等和不等式:

  • 平等: ISNULL(NULLIF(A, B), NULLIF(B, A)) IS NULL
  • Inequaltiy: ISNULL(NULLIF(A, B), NULLIF(B, A)) IS NOT NULL

这是一个SQL小提琴展示它如何工作http://sqlfiddle.com/#!3/471d60/1

 IF EXISTS(SELECT * FROM MY_TABLE WHERE (MY_FIELD1 = @IN_MY_FIELD1 or (MY_FIELD1 IS NULL and @IN_MY_FIELD1 is NULL)) AND (MY_FIELD2 = @IN_MY_FIELD2 or (MY_FIELD2 IS NULL and @IN_MY_FIELD2 is NULL)) AND (MY_FIELD3 = @IN_MY_FIELD3 or (MY_FIELD3 IS NULL and @IN_MY_FIELD3 is NULL)) AND (MY_FIELD4 = @IN_MY_FIELD4 or (MY_FIELD4 IS NULL and @IN_MY_FIELD4 is NULL)) AND (MY_FIELD5 = @IN_MY_FIELD5 or (MY_FIELD5 IS NULL and @IN_MY_FIELD5 is NULL)) AND (MY_FIELD6 = @IN_MY_FIELD6 or (MY_FIELD6 IS NULL and @IN_MY_FIELD6 is NULL))) BEGIN goto on_duplicate END 

Wordy与IFNULL / COALESCE解决scheme相比。 但是,不用考虑什么值不会出现在可以用作NULL的数据中。

你可以合并每一个值,但是这有点诱人:

  IF EXISTS(SELECT * FROM MY_TABLE WHERE coalesce(MY_FIELD1,'MF1') = coalesce(@IN_MY_FIELD1,'MF1') AND ... BEGIN goto on_duplicate END 

您还需要确保coalesced值在所讨论的列上不是有效的值。 例如,如果MY_FIELD1的值可能是“MF1”,那么这将导致很多虚假命中。

你在你的字段上创build一个主键,让引擎执行唯一性。 不pipe怎样,EXISTS的逻辑是不正确的,因为在比赛条件下是有缺陷的。

如果你想对不相等的值进行比较呢? 只是在前面提到的比较之前使用“NOT”不起作用。 我能想到的最好的是:

 (Field1 <> Field2) OR (NULLIF(Field1, Field2) IS NOT NULL) OR (NULLIF(Field2, Field1) IS NOT NULL) 

NULLIF(TARGET.relation_id,SOURCE.app_relation_id)IS NULL简单的解决scheme

你将不得不使用IS NULL或ISNULL。 真的没有一个在附近。

您可以使用SET ANSI_NULLS来指定Equals(=)和Not Equal To(<>)比较运算符与空值一起使用时的行为。