it-swarm.com.de

Umschreiben IS UNTERSCHEIDEN VON und IS NICHT UNTERSCHEIDEN VON?

Wie können Sie Ausdrücke mit den Standardoperatoren IS DISTINCT FROM und IS NOT DISTINCT FROM in SQL-Implementierungen wie Microsoft SQL Server 2008R2 umschreiben, die diese nicht unterstützen?

43
Jason Kresowaty

Das Prädikat IS DISTINCT FROM wurde als Feature T151 von SQL: 1999 eingeführt, und seine lesbare Negation IS NOT DISTINCT FROM wurde als Feature T152 von SQL: 2003 hinzugefügt. Der Zweck dieser Prädikate besteht darin, sicherzustellen, dass das Ergebnis des Vergleichs zweier Werte entweder True oder False , never Unknown ist.

Diese Prädikate arbeiten mit jedem vergleichbaren Typ (einschließlich Zeilen, Arrays und Multisets), was es sehr schwierig macht, sie exakt zu emulieren. Die meisten dieser Typen werden von SQL Server jedoch nicht unterstützt. Daher können wir ziemlich weit kommen, indem Sie nach ungültigen Argumenten/Operanden suchen:

  • a IS DISTINCT FROM b kann umgeschrieben werden als:

    ((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 kann umgeschrieben werden als:

    (NOT (a <> b OR a IS NULL OR b IS NULL) OR (a IS NULL AND b IS NULL))
    

Ihre eigene Antwort ist falsch, da sie nicht berücksichtigt, dass FALSE OR NULL zu Unknown ausgewertet wird. Beispielsweise sollte NULL IS DISTINCT FROM NULL zu False ausgewertet werden. Ebenso sollte 1 IS NOT DISTINCT FROM NULL zu False ausgewertet werden. In beiden Fällen ergeben Ihre Ausdrücke Unknown .

36
Chris Bandy

Eine andere Lösung, die ich mag, nutzt das echte zweiwertige boolesche Ergebnis von EXISTS in Kombination mit INTERSECT. Diese Lösung sollte in SQL Server 2005 und höher funktionieren.

  • a IS NOT DISTINCT FROM b kann geschrieben werden als:

    EXISTS(SELECT a INTERSECT SELECT b)

Wie dokumentiert, behandelt INTERSECT zwei NULL-Werte als gleich. Wenn also beide NULL sind, ergibt INTERSECT eine einzige Zeile. Daher liefert EXISTS den Wert true.

  • a IS DISTINCT FROM b kann geschrieben werden als:

    NOT EXISTS(SELECT a INTERSECT SELECT b)

Dieser Ansatz ist viel prägnanter, wenn Sie mehrere nullfähige Spalten haben, die Sie in zwei Tabellen vergleichen müssen. Um beispielsweise Zeilen in TableB zurückzugeben, die andere Werte für Col1, Col2 oder Col3 als TableA haben, kann Folgendes verwendet werden:

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

Paul White erläutert diese Problemumgehung ausführlicher: http://web.archive.org/web/20180422151947/http://sqlblog.com:80/blogs/paul_white/archive/2011/06/ 22/undokumentierte-Abfrage-Pläne-Gleichheit-Vergleiche.aspx

24
John Keller

Wenn Ihre SQL-Implementierung die SQL-Standardoperatoren IS DISTINCT FROM und IS NOT DISTINCT FROM nicht implementiert, können Sie Ausdrücke mit den folgenden Äquivalenzen neu schreiben:

Im Algemeinen:

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

Diese Antwort ist falsch, wenn sie in einem Kontext verwendet wird, in dem der Unterschied zwischen UNBEKANNT und FALSCH von Bedeutung ist. Ich finde das allerdings ungewöhnlich. Sehen Sie sich die akzeptierte Antwort von @ChrisBandy an.

Wenn ein Platzhalterwert identifiziert werden kann, der in den Daten nicht tatsächlich vorkommt, ist COALESCE eine Alternative:

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

Eine Einschränkung beim Umschreiben von IS DISTINCT FROM und IS NOT DISTINCT FROM wäre, die Verwendung von Indizes nicht zu beeinträchtigen, zumindest bei der Verwendung von SQL Server. Mit anderen Worten, wenn Sie Folgendes verwenden:

WHERE COALESCE(@input, x) = COALESCE(column, x)

SQL Server kann keinen Index verwenden, der column enthält. In einer WHERE-Klausel wäre es daher vorzuziehen, das Formular zu verwenden

WHERE @input = column OR (@input IS NULL AND column IS NULL)

alle Indizes für column nutzen. (Parens nur zur Klarheit verwendet)

5
Boyd

Für die Referenz wäre die kanonischste (und lesbarste) Implementierung von IS [ NOT ] DISTINCT FROM ein gut formatierter CASE-Ausdruck. Für 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

Offensichtlich sind andere Lösungen (insbesondere John Kellers , die INTERSECT verwenden) kürzer.

Weitere Details hier

1
Lukas Eder

Nur um John Kellers Antwort zu erweitern. Ich bevorzuge das EXISTS- und EXCEPT-Muster:

a IS DISTINCT FROM b
<=>
EXISTS (SELECT a EXCEPT SELECT b)
-- NOT EXISTS (SELECT a INTERSECT SELECT b)

und

a IS NOT DISTINCT FROM  b
<=>
NOT EXISTS (SELECT a EXCEPT SELECT b)
-- EXISTS (SELECT a INTERSECT SELECT b)

aus einem bestimmten Grund. NOT ist ausgerichtet, während es mit INTERSECT invertiert ist.


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

kann umgeschrieben werden als:

(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

kann umgeschrieben werden als:

NOT (a IS NOT DISTINCT FROM b)
0
wojtek

Diese Ausdrücke können ein guter Ersatz für die IS DISTINCT FROM-Logik sein und eine bessere Leistung als die vorherigen Beispiele erbringen, da sie vom SQL-Server zu einem einzigen Prädikatsausdruck kompiliert werden. die Hälfte der Operatorkosten für einen Filterausdruck. Sie sind im Wesentlichen die gleichen wie die von Chris Bandy bereitgestellten Lösungen, jedoch verwenden sie verschachtelte ISNULL- und NULLIF-Funktionen, um die zugrunde liegenden Vergleiche durchzuführen. 

(... natürlich könnte ISNULL durch COALESCE ersetzt werden, wenn Sie möchten)

  • a IS DISTINCT FROM b kann umgeschrieben werden als:

    ISNULL(NULLIF(a, b), NULLIF(b, a)) IS NOT NULL

  • a IS NOT DISTINCT FROM b kann umgeschrieben werden als:

    ISNULL(NULLIF(a, b), NULLIF(b, a)) IS NULL

0
Jason

Dies ist eine alte Frage und es gibt eine neue Antwort. Es ist einfacher zu verstehen und zu pflegen.

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

Es ist zu beachten, dass diese Syntaxalternative zu IS [NOT] DISTINCT FROM funktioniert in allen gängigen SQL-Datenbanken (siehe Link am Ende). Dies und die Alternativen werden ausführlich erklärt hier

0
oᴉɹǝɥɔ