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?
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?
Antworten:
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.id
einen 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 id
Spalten 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)
Alternativ können Sie zwei Nullen INTERSECT
als 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 FROM
Operator ist, der in Oracle noch nicht unterstützt wird.
Der Vollständigkeit halber möchte ich erwähnen, dass die Funktion SYS_OP_MAP_NONNULL
jetzt 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 .
Sie können Nullwerte mit decode verbinden:
on decode(t1.id, t2.id, 1, 0) = 1
decode
behandelt 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)
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 NULL
fü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.
NULL = anything
führt dazu, NULL
weil der SQL-Standard dies sagt. Eine Zeile erfüllt die Join-Bedingung nur, wenn der Ausdruck wahr ist.
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.
UNKNOWN
, nicht FALSE
;)
where (a = b or (a is null and b is null))
Punkt. Das sind meine Gedanken darüber. Ich würde nicht in Betracht ziehensys_op_map_nonnull
, diesen Mann hinter dem Vorhang zu ignorieren. "