我想确保我没有在我的表中插入重复的行(例如,只有主键不同)。 我的所有字段都允许NULLS,因为我认为null意味着 "所有值"。 因为null,我的存储过程中的以下语句不能工作。
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 IS 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
请注意,如果你创建了一个 UNIQUE
索引在你的领域,你的生活将变得更加简单。
同样的道理 @Eric的回答但不使用 'NULL'
符号。
(Field1 = Field2) OR (ISNULL(Field1, Field2) IS NULL)
只有当两个值都是 non-NULL
,并且相互相等,或者两个值都是 NULL
我在做MERGE的时候也需要类似的比较。
WHEN MATCHED AND (Target.Field1 <> Source.Field1 OR ...)
额外的检查是为了避免更新所有列都相同的行。为了我的目的,我想要 NULL <> anyValue
是真的,而且 NULL <> NULL
为False。
解决方案演变如下。
第一次尝试:
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
)
同样的ISNULLNULLIF逻辑可以用来测试平等和不平等。
ISNULL(NULLIF(A, B), NULLIF(B, A)) IS NULL
ISNULL(NULLIF(A, B), NULLIF(B, A)) IS NOT NULL
这里有一个SQL-Fiddle来演示它是如何工作的。http:/sqlfiddle.com#!3471d601
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
啰嗦 相比于IFNULLCOALESCE的解决方案。但会工作,不用考虑什么值不会出现在数据中,可以作为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',那么这将会导致很多虚假的点击。
如果你想对不相等的值进行比较呢?仅仅在前面提到的比较前面使用 "NOT "是行不通的。我能想到的最好的办法是
(Field1 <> Field2) OR (NULLIF(Field1, Field2) IS NOT NULL) OR (NULLIF(Field2, Field1) IS NOT NULL)
你在你的字段上创建一个主键,让引擎强制执行唯一性。反正做IF EXISTS逻辑是不正确的,因为在竞赛条件下是有缺陷的。
等于 比较。
((f1 IS NULL AND f2 IS NULL) OR (f1 IS NOT NULL AND f2 IS NOT NULL AND f1 = f2))
不等于 比较。就把它否定了 等于 上面的比较。
NOT ((f1 IS NULL AND f2 IS NULL) OR (f1 IS NOT NULL AND f2 IS NOT NULL AND f1 = f2))
是否啰嗦?是的,它是。但是它很有效率,因为它不调用任何函数。我们的想法是在谓词中使用短路,以确保等价操作符(=)只用于非空值,否则null会在表达式树中向上传播。
你可以使用 SET ANSI_NULLS
以指定等于(=)和不等于(<>)比较运算符在使用空值时的行为。
你必须使用IS NULL或ISNULL。 真的是没有办法了。
NULLIF(TARGET.relation_id, SOURCE.app_relation_id) IS NULL 简单的解决方案。
上文中,@drowa 展示了一种我同意的啰嗦方法。 它很好,因为它避免了3值逻辑问题。 这里提供的许多其他方法在被否定时,会以微妙和意想不到的方式失败,因为它们是在处理 null
等于 false
其中 它不是.
然而,我有一个工作流程,我觉得这样做很方便,这里有一个regex。 给定代码的形式是
(leftSide <=> rightSide)
在regex中找到这个。
\(([a-zA-Z0-9_.@]+)\s*<=>\s*([a-zA-Z0-9_.@]+)\)
然后用这个代替
(/*$1 <=> $2*/ ($1 IS NULL AND $2 IS NULL) OR ($1 IS NOT NULL AND $2 IS NOT NULL AND $1 = $2))
所以我写了 (leftSide <=> rightSide)
代码,并应用上面的regex转换来获得扩展的形式。 如果MSSQL提供了某种宏扩展功能就更好了,这样我就不用手动操作了,但它没有。
引用了@Drowa的答案,供参考。
等于比较:
((f1 IS NULL AND f2 IS NULL) OR (f1 IS NOT NULL AND f2 IS NOT NULL AND f1 = f2))
不等于比较。就把上面的Equals比较否定了。
NOT ((f1 IS NULL AND f2 IS NULL) OR (f1 IS NOT NULL AND f2 IS NOT NULL AND f1 = f2))
是不是很啰嗦?是的,它是。但是它很有效率,因为它不调用任何函数。我们的想法是在谓词中使用短路,以确保等价操作符(=)只用于非空值,否则null会在表达式树中向上传播。