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

时间:2009-07-02 15:42:18

标签: sql sql-server tsql stored-procedures

我想确保我没有在表格中插入重复的行(例如,只有主键不同)。我的所有字段都允许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语句的情况下检查重复项?

14 个答案:

答案 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'之类的内容。

应该注意的是,有两个参数,ISNULLCOALESCE的工作方式相同,如果您有一些要测试的值(例如.- 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)