我想确保我没有在表格中插入重复的行(例如,只有主键不同)。我的所有字段都允许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语句的情况下检查重复项?
答案 0 :(得分:65)
与@Eric's answer相同,但不使用'NULL'
符号。
(Field1 = Field2) OR (ISNULL(Field1, Field2) IS NULL)
只有当这两个值均为non-NULL
且彼此相等,或两个值均为NULL
答案 1 :(得分:46)
使用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
索引,那么您的生活将会更加简单。
答案 2 :(得分:32)
使用ISNULL
:
ISNULL(MY_FIELD1, 'NULL') = ISNULL(@IN_MY_FIELD1, 'NULL')
如果更有意义,您可以将'NULL'
更改为'All Values'
之类的内容。
应该注意的是,有两个参数,ISNULL
与COALESCE
的工作方式相同,如果您有一些要测试的值(例如.- COALESCE(@IN_MY_FIELD1, @OtherVal, 'NULL')
),您可以使用它。 COALESCE
也会在第一个非null后返回,这意味着如果你期望MY_FIELD1为空,它会(稍微)更快。但是,我发现ISNULL
更具可读性,所以这就是我在这里使用它的原因。
答案 3 :(得分:31)
我在做MERGE时需要进行类似的比较:
WHEN MATCHED AND (Target.Field1 <> Source.Field1 OR ...)
其他检查是为了避免更新所有列已经相同的行。出于我的目的,我希望NULL <> anyValue
为True,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's answer启发):
WHEN MATCHED AND
(
ISNULL(
NULLIF(Target.Field1, Source.Field1),
NULLIF(Source.Field1, Target.Field1)
) IS NOT NULL
-- OR ... Repeat for other columns
)
可以使用相同的ISNULL / NULLIF逻辑来测试相等性和不等式:
ISNULL(NULLIF(A, B), NULLIF(B, A)) IS NULL
ISNULL(NULLIF(A, B), NULLIF(B, A)) IS NOT NULL
这是一个SQL-Fiddle演示它是如何工作的http://sqlfiddle.com/#!3/471d60/1
答案 4 :(得分:20)
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解决方案相比。但是,无需考虑哪些值不会出现在可以用作NULL的数据中。
答案 5 :(得分:10)
你可以合并每个值,但它有点畏缩:
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',那么这将导致大量的虚假命中。
答案 6 :(得分:7)
如果您想对不相等的值进行比较,该怎么办?只是在前面提到的比较前使用“NOT”不起作用。我能想到的最好的是:
(Field1 <> Field2) OR (NULLIF(Field1, Field2) IS NOT NULL) OR (NULLIF(Field2, Field1) IS NOT NULL)
答案 7 :(得分:6)
您可以在字段上创建主键,并让引擎强制执行唯一性。执行IF EXISTS逻辑无论如何都是错误的,因为有竞争条件的缺陷。
答案 8 :(得分:2)
您可以使用SET ANSI_NULLS
来指定Equals(=)和Not Equal To(&lt;&gt;)比较运算符与null值一起使用时的行为。
答案 9 :(得分:1)
等于比较:
((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值,否则null将在表达式树中向上传播。
答案 10 :(得分:1)
上面,@ drowa显示了我同意的详细方法。很好,因为它避免了三值逻辑问题。否定时,此处提供的许多其他方法将以微妙和意外的方式失败,因为它们将null
等同于false
,而不是。
但是,我有一个工作流程,使它变得很方便,这是一个正则表达式。给定形式的代码
(leftSide <=> rightSide)
正则表达式找到了这个
\(([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)
代码,并应用了上述正则表达式转换来获取扩展形式。如果MSSQL提供某种宏扩展,这样更好,这样我就不必手动进行操作了,但事实并非如此。
@Drowa的答案供参考:
等于比较:
((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值, 否则null将在表达式树中向上传播。
答案 11 :(得分:0)
您必须使用IS NULL或ISNULL。周围真的没有了。
答案 12 :(得分:0)
NULLIF(TARGET.relation_id,SOURCE.app_relation_id)IS NULL简单解决方案
答案 13 :(得分:-2)