Warum kann ich in Joins keine Nullwerte verwenden?

13

Ich habe das Abfrageproblem mit ... row_number() over (partition by... gelöst. Dies ist eine allgemeinere Frage, warum wir in Joins keine Spalten mit Nullwerten verwenden können. Warum kann eine Null für einen Join nicht gleich einer Null sein?

Mark Warner
quelle

Antworten:

31

Warum kann eine Null für einen Join nicht gleich einer Null sein?

Sagen Sie Oracle einfach, dass es das tun soll:

select *
from one t1 
  join two t2 on coalesce(t1.id, -1) = coalesce(t2.id, -1);

(Beachten Sie, dass Sie in Standard-SQL t1.id is not distinct from t2.ideinen nullsicheren Gleichheitsoperator erhalten können, Oracle unterstützt dies jedoch nicht.)

Dies funktioniert jedoch nur, wenn der Ersatzwert (-1 im obigen Beispiel) nicht tatsächlich in der Tabelle angezeigt wird. Es mag möglich sein, einen solchen "magischen" Wert für Zahlen zu finden, für Zeichenwerte ist dies jedoch sehr schwierig (insbesondere, weil Oracle auch eine leere Zeichenfolge behandelt null).

Plus: Es wird kein Index für die idSpalten verwendet (Sie können jedoch einen funktionsbasierten Index mit dem coalesce()Ausdruck definieren).

Eine weitere Option, die für alle Typen ohne magische Werte funktioniert:

              on t1.id = t2.id or (t1.id is null and t2.id is null)

Aber die eigentliche Frage ist: Ist das sinnvoll?

Betrachten Sie die folgenden Beispieldaten:

Tisch eins

id
----
1
2
(null)
(null)

Tisch zwei

id
----
1
2
(null)
(null)
(null)

Welche Kombination von Nullwerten sollte im Join ausgewählt werden? Das obige Beispiel führt zu einer Art Cross-Join für alle Nullwerte.

T1_ID  | T2_ID 
-------+-------
     1 |      1
     2 |      2
(null) | (null)
(null) | (null)
(null) | (null)
(null) | (null)
(null) | (null)
(null) | (null)
ein Pferd ohne Name
quelle
6

Alternativ können Sie zwei Nullen INTERSECTals Gleichheitsoperator aufeinander abstimmen lassen :

SELECT
  *
FROM
  t1
  INNER JOIN t2
    ON EXISTS (SELECT t1.ID FROM DUAL INTERSECT SELECT t2.ID FROM DUAL)
;

Eine Illustration finden Sie in dieser DBFiddle-Demo .

Natürlich sieht das ziemlich mundvoll aus, obwohl es eigentlich nicht viel länger ist als der Vorschlag von BriteSponge . Es ist jedoch sicherlich keine Übereinstimmung, wenn Sie das Wortspiel entschuldigen, mit der Kürze der zuvor in Kommentaren erwähnten Standardmethode, die der IS NOT DISTINCT FROMOperator ist, der in Oracle noch nicht unterstützt wird.

Andriy M
quelle
2

Der Vollständigkeit halber möchte ich erwähnen, dass die Funktion SYS_OP_MAP_NONNULLjetzt sicher zum Vergleichen von Werten verwendet werden kann, die null sind, wie es jetzt in der 12c-Dokumentation dokumentiert ist. Dies bedeutet, dass Oracle es nicht zufällig entfernt und Ihren Code zerstört.

SELECT *
FROM   one t1 
       JOIN two t2
         ON SYS_OP_MAP_NONNULL(t1.id) = SYS_OP_MAP_NONNULL(t2.id)

Der Vorteil ist, dass Sie nicht auf das 'magische' Zahlenproblem stoßen.

Die Referenz in den Oracle-Dokumenten befindet sich unter Grundlegende materialisierte Ansichten - Auswählen von Indizes für materialisierte Ansichten .

BriteSponge
quelle
So ist es jetzt dokumentiert? Weil AskTom (im Jahr 2003) erklärte: " - Es ist undokumentiert und birgt daher das Risiko, wegzugehen oder die Funktionalität zu ändern , was ausreicht, um die Leute dazu zu bringen, einfach" aufzuhören ", dorthin zu gehen, und Sie könnten in der nächsten Version wirklich verrückt sein. der einzige RICHTIGE Weg ist: where (a = b or (a is null and b is null)) Punkt. Das sind meine Gedanken darüber. Ich würde nicht in Betracht ziehen sys_op_map_nonnull, diesen Mann hinter dem Vorhang zu ignorieren. "
ypercubeᵀᴹ
Wenn Sie einen Link haben, fügen Sie ihn bitte der Frage hinzu. Ich habe in 12c Functions keine Erwähnung gefunden, aber die Suche nach Oracle-Dokumentation und einer bestimmten Version ist ziemlich schwierig.
ypercubeᵀᴹ
2

Sie können Nullwerte mit decode verbinden:

on decode(t1.id, t2.id, 1, 0) = 1

decodebehandelt Nullen als gleich, so dass dies ohne "magische" Zahlen funktioniert. Die beiden Spalten müssen denselben Datentyp haben.

Es wird nicht den am besten lesbaren Code erstellen, aber wahrscheinlich immer noch besser als t1.id = t2.id or (t1.id is null and t2.id is null)

Tamás Bárász
quelle
1

Warum können Sie in Joins keine Nullwerte verwenden? In Oracle werden beide der folgenden Werte nicht als wahr ausgewertet:

  • NULL = NULL
  • NULL <> NULL

Deshalb haben wir IS NULL/ IS NOT NULLfür NULL - Werte zu überprüfen.
Um dies zu testen, können Sie einfach Folgendes tun:

SELECT * FROM table_name WHERE NULL = NULL

Joins bewerten eine boolesche Bedingung und haben sie nicht so programmiert, dass sie anders funktionieren. Sie können der Join-Bedingung ein Größer-als-Zeichen hinzufügen und andere Bedingungen hinzufügen. Es wird nur als boolescher Ausdruck ausgewertet.

Ich denke, eine Null kann aus Gründen der Konsistenz nicht gleich einer Null in Joins sein. Es würde sich dem üblichen Verhalten des Vergleichsoperators widersetzen.

Andrew Puglionesi
quelle
NULL = anythingführt dazu, NULLweil der SQL-Standard dies sagt. Eine Zeile erfüllt die Join-Bedingung nur, wenn der Ausdruck wahr ist.
Laurenz Albe
1
Über die Details der Literalimplementierung hinaus (was nicht immer der Fall ist: Einige DBs haben die Option, NULL für einige / alle Zwecke mit NULL gleichzusetzen) gibt es einen logischen Grund: NULL ist unbekannt. Wenn Sie NULL mit NULL vergleichen, fragen Sie "Ist dieses unbekannte Ding gleich diesem anderen unbekannten Ding?", Worauf die einzig vernünftige Antwort "unbekannt" lautet - ein weiteres NULL (das in einer Vergleichssituation auf false abgebildet wird).
David Spillett
-4

Ein Nullwert in den meisten relationalen Datenbanken wird als UNBEKANNT betrachtet. Es ist nicht mit allen HEX-Nullen zu verwechseln. Wenn etwas null enthält (unbekannt), können Sie es nicht vergleichen.

Unknown = Known False
Unknown = Unknown False
Unknown >= Known False
Known >= Unknown False

Dies bedeutet, dass der else-Teil immer wahr ist, wenn Sie eine Null als Operanden in einem booleschen Ausdruck haben.

Entgegen dem allgemeinen Hass der Entwickler gegen null hat null seinen Platz. Wenn etwas unbekannt ist, verwenden Sie null.

Jujiro
quelle
6
Eigentlich all Beispiel Vergleichen Sie haben, Ertrag UNKNOWN, nicht FALSE;)
ypercubeᵀᴹ
Sie haben Recht, aber der Zweck eines booleschen Ausdrucks ist, nur wahr oder falsch zu ergeben. Lassen Sie uns hier also nicht verrückt werden :).
Jujiro