如何在T-SQL中比较可能都是空的值?

问题描述 投票:69回答:14

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

sql sql-server tsql stored-procedures
14个回答
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

请注意,如果你创建了一个 UNIQUE 索引在你的领域,你的生活将变得更加简单。


78
投票

同样的道理 @Eric的回答但不使用 'NULL' 符号。

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

只有当两个值都是 non-NULL,并且相互相等,或者两个值都是 NULL


38
投票

我在做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逻辑可以用来测试平等和不平等。

  • Equality: 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#!3471d601


32
投票

使用 ISNULL:

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

你可以改变 'NULL' 到类似 'All Values' 如果这样做更有意义。

需要注意的是,有了两个论点。ISNULL 同理 COALESCE,如果你有几个值需要测试,你可以用它来测试(如-)。COALESCE(@IN_MY_FIELD1, @OtherVal, 'NULL')). COALESCE 也会在第一个非空值之后返回,这意味着如果你希望MY_FIELD1是空值的话,它的速度会快一些。然而,我发现 ISNULL 可读性更强,所以我才用它,这里。


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

啰嗦 相比于IFNULLCOALESCE的解决方案。但会工作,不用考虑什么值不会出现在数据中,可以作为null的替身。


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',那么这将会导致很多虚假的点击。


7
投票

如果你想对不相等的值进行比较呢?仅仅在前面提到的比较前面使用 "NOT "是行不通的。我能想到的最好的办法是

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

6
投票

你在你的字段上创建一个主键,让引擎强制执行唯一性。反正做IF EXISTS逻辑是不正确的,因为在竞赛条件下是有缺陷的。


3
投票

等于 比较。

((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会在表达式树中向上传播。


2
投票

你可以使用 SET ANSI_NULLS 以指定等于(=)和不等于(<>)比较运算符在使用空值时的行为。


0
投票

你必须使用IS NULL或ISNULL。 真的是没有办法了。


0
投票

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


0
投票

上文中,@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会在表达式树中向上传播。


-2
投票
© www.soinside.com 2019 - 2024. All rights reserved.