それらをサポートしないMicrosoft SQL Server 2008R2などのSQL実装で、標準のIS DISTINCT FROM
およびIS NOT DISTINCT FROM
演算子を含む式をどのように書き換えますか?
IS DISTINCT FROM
述語はSQL:1999の機能T151として導入され、その読み取り可能な否定IS NOT DISTINCT FROM
はSQL:2003の機能T152として追加されました。これらの述語の目的は、2つの値を比較した結果がTrueまたはFalseであり、決してnknownではないことを保証することです。
これらの述語は、同等のタイプ(行、配列、マルチセットを含む)で機能し、正確にエミュレートすることはかなり複雑になります。ただし、SQL Serverはこれらのタイプのほとんどをサポートしていないため、nullの引数/オペランドをチェックすることでかなり遠くまで到達できます。
a IS DISTINCT FROM b
は次のように書き換えられます。
((a <> b OR a IS NULL OR b IS NULL) AND NOT (a IS NULL AND b IS NULL))
a IS NOT DISTINCT FROM b
は次のように書き換えられます。
(NOT (a <> b OR a IS NULL OR b IS NULL) OR (a IS NULL AND b IS NULL))
FALSE OR NULL
がnknownと評価されると見なすことができないため、あなた自身の答えは正しくありません。たとえば、NULL IS DISTINCT FROM NULL
はFalseと評価されます。同様に、1 IS NOT DISTINCT FROM NULL
はFalseと評価されます。どちらの場合も、式の結果はnknownになります。
私が好む別のソリューションは、INTERSECTと組み合わせたEXISTSの真の2値ブール結果を利用します。このソリューションはSQL Server 2005以降で機能します。
_a IS NOT DISTINCT FROM b
_は次のように書くことができます:
EXISTS(SELECT a INTERSECT SELECT b)
文書化されているように、INTERSECTは2つのNULL値を等しいものとして処理するため、両方がNULLの場合、INTERSECTは単一行になるため、EXISTSはtrueを返します。
_a IS DISTINCT FROM b
_は次のように書くことができます:
NOT EXISTS(SELECT a INTERSECT SELECT b)
このアプローチは、2つのテーブルで比較する必要のあるnull許容可能な列が複数ある場合、はるかに簡潔になります。たとえば、TableAとは異なるCol1、Col2、またはCol3の値を持つTableBの行を返すには、次のように使用できます。
_SELECT *
FROM TableA A
INNER JOIN TableB B ON A.PK = B.PK
WHERE NOT EXISTS(
SELECT A.Col1, A.Col2, A.Col3
INTERSECT
SELECT B.Col1, B.Col2, B.Col3);
_
ポールホワイトはこの回避策をより詳細に説明しています: http://web.archive.org/web/20180422151947/http://sqlblog.com:80/blogs/paul_white/archive/2011/06/22/undocumented -query-plans-equality-comparisons.aspx
SQL実装がSQL標準を実装していない場合IS DISTINCT FROM
およびIS NOT DISTINCT FROM
演算子を使用すると、次の同値を使用してそれらを含む式を書き換えることができます。
一般に:
a IS DISTINCT FROM b <==>
(
((a) IS NULL AND (b) IS NOT NULL)
OR
((a) IS NOT NULL AND (b) IS NULL)
OR
((a) <> (b))
)
a IS NOT DISTINCT FROM b <==>
(
((a) IS NULL AND (b) IS NULL)
OR
((a) = (b))
)
UNKNOWNとFALSEの違いが重要なコンテキストで使用すると、この回答は正しくありません。でもそれは珍しいと思います。 @ChrisBandyが承認した回答を参照してください。
データに実際には存在しないプレースホルダー値を特定できる場合は、COALESCE
が代わりになります。
a IS DISTINCT FROM b <==> COALESCE(a, placeholder) <> COALESCE(b, placeholder)
a IS NOT DISTINCT FROM b <==> COALESCE(a, placeholder) = COALESCE(b, placeholder)
IS DISTINCT FROMおよびIS NOT DISTINCT FROMを書き換えると、少なくともSQL Serverを使用している場合は、インデックスの使用が妨げられないはずです。言い換えると、以下を使用する場合:
WHERE COALESCE(@input, x) = COALESCE(column, x)
SQL Serverはcolumnを含むインデックスを使用できません。したがって、WHERE句では、次の形式を使用することをお勧めします。
WHERE @input = column OR (@input IS NULL AND column IS NULL)
columnのインデックスを利用する。 (括弧は明確にするためにのみ使用されます)
参考までに、IS [ NOT ] DISTINCT FROM
の最も標準的な(そして読みやすい)実装は、適切にフォーマットされたCASE
式です。 IS DISTINCT FROM
の場合:
CASE WHEN [a] IS NULL AND [b] IS NULL THEN FALSE
WHEN [a] IS NULL AND [b] IS NOT NULL THEN TRUE
WHEN [a] IS NOT NULL AND [b] IS NULL THEN TRUE
WHEN [a] = [b] THEN FALSE
ELSE TRUE
END
明らかに、他のソリューション(具体的には John Keller's 、INTERSECT
を使用)の方が簡潔です。
ただ拡張する John Keller's 答え。私はEXISTS
およびEXCEPT
パターンを使用することを好みます:
a IS DISTINCT FROM b
<=>
EXISTS (SELECT a EXCEPT SELECT b)
-- NOT EXISTS (SELECT a INTERSECT SELECT b)
そして
a IS NOT DISTINCT FROM b
<=>
NOT EXISTS (SELECT a EXCEPT SELECT b)
-- EXISTS (SELECT a INTERSECT SELECT b)
特定の理由で。 NOT
は整列されますが、INTERSECT
は反転されます。
SELECT 1 AS PK, 21 AS c, NULL AS b
INTO tab1;
SELECT 1 AS PK, 21 AS c, 2 AS b
INTO tab2;
SELECT *
FROM tab1 A
JOIN tab2 B ON A.PK = B.PK
WHERE EXISTS(SELECT A.c, A.B
EXCEPT
SELECT B.c, B.b);
a IS NOT DISTINCT FROM b
次のように書き換えることができます。
(a IS NOT NULL AND b IS NOT NULL AND a=b) OR (a IS NULL AND b is NULL)
a IS DISTINCT FROM b
次のように書き換えることができます。
NOT (a IS NOT DISTINCT FROM b)
これらの式はIS DISTINCT FROMロジックの良い代替品であり、SQLサーバーによって単一の述語式にコンパイルされて約半分になるため、前の例よりも優れたパフォーマンスを発揮します。フィルター式の演算子コスト。これらは基本的にChris Bandyが提供するソリューションと同じですが、ネストされたISNULLおよびNULLIF関数を使用して、基礎となる比較を実行します。
(... ISNULLはCOALESCEで置き換えることもできます)
_a IS DISTINCT FROM b
_は次のように書き換えられます。
ISNULL(NULLIF(a, b), NULLIF(b, a)) IS NOT NULL
_a IS NOT DISTINCT FROM b
_は次のように書き換えられます。
ISNULL(NULLIF(a, b), NULLIF(b, a)) IS NULL
これは古い質問であり、新しい答えがあります。理解と保守が簡単です。
-- a IS DISTINCT FROM b
CASE WHEN (a = b) OR (a IS NULL AND b IS NULL) THEN 1 ELSE 0 END = 0
-- a IS NOT DISTINCT FROM b
CASE WHEN (a = b) OR (a IS NULL AND b IS NULL) THEN 1 ELSE 0 END = 1
IS [NOT] DISTINCT FROM
に代わるこの構文は、すべての主要なSQLデータベースで機能することに注意してください(最後のリンクを参照)。これと代替案は入念に説明されています here