web-dev-qa-db-ja.com

両方がnullの可能性がある値を比較する方法はT-SQLです

テーブルに重複する行を挿入しないようにしたい(たとえば、主キーのみが異なる)。 nullは「すべての値」を意味すると決めたため、すべてのフィールドで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ステートメントがなくても重複をチェックするにはどうすればよいですか?

67
srmark

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インデックスを作成すると、人生がずっと簡単になることに注意してください。

47
Quassnoi

@ Eric's answer と同じ行に沿って、ただし'NULL'シンボルを使用しません。

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

これは、両方の値がnon-NULLであり、互いに等しい場合、または両方の値がNULLである場合にのみ真になります。

68
Graeme Job

ISNULL を使用します。

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

_'NULL'_の方が意味がある場合は、_'All Values'_のようなものに変更できます。

2つの引数がある場合、ISNULLCOALESCE と同じように機能し、テストする値がいくつかある場合に使用できます(つまり、-COALESCE(@IN_MY_FIELD1, @OtherVal, 'NULL'))。 COALESCEは、最初のNULL以外の後にも戻ります。つまり、MY_FIELD1が空白であると予想される場合、(わずかに)高速になります。ただし、ISNULLの方がはるかに読みやすいので、ここで使用しました。

32
Eric

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
)
_

2回目の試行:

_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
)
_

3回目の試行( @ THEnの答え に触発された):

_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

31
WileCau
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

IFNULL/COALESCEソリューションと比較して。ただし、NULLの代用として使用できるデータにどの値が表示されないかを考慮する必要はありません。

20

各値を合体させることもできますが、それは少しひるみを誘発します:

    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」になる可能性がある場合、これにより多くの偽のヒットが発生します。

10
butterchicken

等しくない値の比較を行いたい場合はどうしますか?前述の比較の前に「NOT」を使用するだけでは機能しません。私が思いつくことができる最高のものは:

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

フィールドに主キーを作成し、エンジンに一意性を強制させます。競合状態に欠陥があるため、とにかくIF EXISTSロジックを実行することは正しくありません。

6
Remus Rusanu

SET ANSI_NULLSを使用して、等しい値(=)および等しくない(<>)比較演算子をnull値で使用する場合の動作を指定できます。

2
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が伝播します。

1
drowa

NULLIF(TARGET.relation_id、SOURCE.app_relation_id)IS NULLシンプルなソリューション

0
user3763117

IS NULLまたはISNULLを使用する必要があります。実際にその周りにはありません。

0