web-dev-qa-db-ja.com

IS DISTINCT FROMおよびIS NOT DISTINCT FROMではないですか?

それらをサポートしないMicrosoft SQL Server 2008R2などのSQL実装で、標準のIS DISTINCT FROMおよびIS NOT DISTINCT FROM演算子を含む式をどのように書き換えますか?

49
Jason Kresowaty

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 NULLnknownと評価されると見なすことができないため、あなた自身の答えは正しくありません。たとえば、NULL IS DISTINCT FROM NULLFalseと評価されます。同様に、1 IS NOT DISTINCT FROM NULLFalseと評価されます。どちらの場合も、式の結果はnknownになります。

43
Chris Bandy

私が好む別のソリューションは、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

26
John Keller

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)
11
Jason Kresowaty

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のインデックスを利用する。 (括弧は明確にするためにのみ使用されます)

5
Boyd

参考までに、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'sINTERSECTを使用)の方が簡潔です。

詳細はこちら

1
Lukas Eder

ただ拡張する 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);

DBFiddle Demo

1
Lukasz Szozda
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)
0
wojtek

これらの式は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

0
Jason

これは古い質問であり、新しい答えがあります。理解と保守が簡単です。

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

0
oᴉɹǝɥɔ