テーブルに重複する行を挿入しないようにしたい(たとえば、主キーのみが異なる)。 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ステートメントがなくても重複をチェックするにはどうすればよいですか?
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
インデックスを作成すると、人生がずっと簡単になることに注意してください。
@ Eric's answer と同じ行に沿って、ただし'NULL'
シンボルを使用しません。
(Field1 = Field2) OR (ISNULL(Field1, Field2) IS NULL)
これは、両方の値がnon-NULL
であり、互いに等しい場合、または両方の値がNULL
である場合にのみ真になります。
ISNULL
を使用します。
_ISNULL(MY_FIELD1, 'NULL') = ISNULL(@IN_MY_FIELD1, 'NULL')
_
_'NULL'
_の方が意味がある場合は、_'All Values'
_のようなものに変更できます。
2つの引数がある場合、ISNULL
は COALESCE
と同じように機能し、テストする値がいくつかある場合に使用できます(つまり、-COALESCE(@IN_MY_FIELD1, @OtherVal, 'NULL')
)。 COALESCE
は、最初のNULL以外の後にも戻ります。つまり、MY_FIELD1が空白であると予想される場合、(わずかに)高速になります。ただし、ISNULL
の方がはるかに読みやすいので、ここで使用しました。
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
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の代用として使用できるデータにどの値が表示されないかを考慮する必要はありません。
各値を合体させることもできますが、それは少しひるみを誘発します:
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」になる可能性がある場合、これにより多くの偽のヒットが発生します。
等しくない値の比較を行いたい場合はどうしますか?前述の比較の前に「NOT」を使用するだけでは機能しません。私が思いつくことができる最高のものは:
(Field1 <> Field2) OR (NULLIF(Field1, Field2) IS NOT NULL) OR (NULLIF(Field2, Field1) IS NOT NULL)
フィールドに主キーを作成し、エンジンに一意性を強制させます。競合状態に欠陥があるため、とにかくIF EXISTSロジックを実行することは正しくありません。
SET ANSI_NULLS
を使用して、等しい値(=)および等しくない(<>)比較演算子をnull値で使用する場合の動作を指定できます。
等しい比較:
((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が伝播します。
NULLIF(TARGET.relation_id、SOURCE.app_relation_id)IS NULLシンプルなソリューション
IS NULLまたはISNULLを使用する必要があります。実際にその周りにはありません。