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

srm*_*ark 68 sql t-sql sql-server 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
Run Code Online (Sandbox Code Playgroud)

因为NULL = NULL不是真的.

如何在没有每列的IF IS NULL语句的情况下检查重复项?

小智 69

@ Eric的答案一样,但没有使用'NULL'符号.

(Field1 = Field2) OR (ISNULL(Field1, Field2) IS NULL)
Run Code Online (Sandbox Code Playgroud)

只有当两个值non-NULL相同且彼此相等或两个值都相同时,才会出现这种情况NULL

  • 对于等式测试来说,这绝对是最严格的(`A = B OR ISNULL(A,B)IS NULL`没有不必要的parens),但由于UNKNOWN无法反转的方式而反转是不安全的.我见过的最短的不平等测试是@WileCau的. (5认同)

Qua*_*noi 48

使用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
Run Code Online (Sandbox Code Playgroud)

请注意,如果您UNIQUE在字段上创建索引,您的生活将会更加简单.

  • 当这种方法被用作单个字段的部分连接谓词时,它的执行速度比等效的"(a = b或(a为空,b为空))"慢得多.字段a和b碰巧是sql_variants,也许这就是原因. (2认同)

Wil*_*Cau 34

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

WHEN MATCHED AND (Target.Field1 <> Source.Field1 OR ...)
Run Code Online (Sandbox Code Playgroud)

附加检查是为了避免更新所有列已经相同的行.为了我的目的,我想NULL <> anyValue成为真,并且NULL <> NULL是假的.

解决方案演变如下:

第一次尝试:

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
)
Run Code Online (Sandbox Code Playgroud)

第二次尝试:

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
)
Run Code Online (Sandbox Code Playgroud)

第三次尝试(受@ THEn的回答启发):

WHEN MATCHED AND
(

    ISNULL(
        NULLIF(Target.Field1, Source.Field1),
        NULLIF(Source.Field1, Target.Field1)
    ) IS NOT NULL

    -- OR ... Repeat for other columns
)
Run Code Online (Sandbox Code Playgroud)

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

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

这是一个SQL-Fiddle演示如何工作http://sqlfiddle.com/#!3/471d60/1

  • 很好的答案,但对我来说,它证明了 3 值逻辑在现实世界中的使用是完全不可原谅的,至少在没有比任何 SQL 方言中存在更多运算符的情况下是这样。 (2认同)

Eri*_*ric 32

用途ISNULL:

ISNULL(MY_FIELD1, 'NULL') = ISNULL(@IN_MY_FIELD1, 'NULL')
Run Code Online (Sandbox Code Playgroud)

您可以更改'NULL'喜欢的东西'All Values',如果它更有意义,这样做.

应该注意的是,有两个参数,ISNULL工作方式相同COALESCE,如果你有一些值要测试(ie- COALESCE(@IN_MY_FIELD1, @OtherVal, 'NULL')),你可以使用它们.COALESCE也会在第一个非null之后返回,这意味着如果你期望MY_FIELD1为空,它会(稍微)更快.但是,我发现ISNULL它更具可读性,所以这就是我在这里使用它的原因.

  • 如果字段的值为"NULL"怎么办? (7认同)
  • 使用不会发生的替代值,例如数字字段的"x"或字符串字段的字符串太长. (2认同)
  • @ l0b0使用太长的字符串将不起作用,因为ISNULL将截断替换值以匹配选中值的大小. (2认同)
  • 这可能会给 Jennifer Null 带来问题:https://gizmodo.com/it-really-sucks-to-be-named-jennifer-null-1767159091 (2认同)

Sha*_*nce 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
Run Code Online (Sandbox Code Playgroud)

Wordy与IFNULL/COALESCE解决方案相比.但是无需考虑哪些值不会出现在可以用作NULL的数据中的数据.


but*_*ken 10

你可以合并每个值,但它有点畏缩:

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

您还需要确保该coalesced值不是相关列上的其他有效值.例如,如果MY_FIELD1的值可能是'MF1',那么这将导致大量的虚假命中.


小智 7

如果你想对不相等的值进行比较怎么办?只是在前面提到的比较前使用"NOT"不起作用.我能想到的最好的是:

(Field1 <> Field2) OR (NULLIF(Field1, Field2) IS NOT NULL) OR (NULLIF(Field2, Field1) IS NOT NULL)
Run Code Online (Sandbox Code Playgroud)


Rem*_*anu 6

您在字段上创建主键,并让引擎强制执行唯一性.执行IF EXISTS逻辑无论如何都是错误的,因为有竞争条件的缺陷.


dro*_*owa 6

相等比较:

((f1 IS NULL AND f2 IS NULL) OR (f1 IS NOT NULL AND f2 IS NOT NULL AND f1 = f2))
Run Code Online (Sandbox Code Playgroud)

不等于比较:只需否定上面的等于比较。

NOT ((f1 IS NULL AND f2 IS NULL) OR (f1 IS NOT NULL AND f2 IS NOT NULL AND f1 = f2))
Run Code Online (Sandbox Code Playgroud)

它很冗长吗?是的。但是它很有效,因为它不调用任何函数。这个想法是在谓词中使用短路来确保相等运算符 (=) 仅用于非空值,否则 null 将在表达式树中向上传播。