Oracle - Tabellenalias und NULL-Auswertung in Join

8

Ich wollte nur ein Beispiel geben, um zu erklären, wie NULL in Oracle zu "unerwarteten" Verhaltensweisen führen kann, aber ich habe etwas gefunden, was ich nicht erwartet hätte ...

Setup:

%Vor%

Dies gibt, was ich erwartet habe:

%Vor%

Wenn ich Tabellenaliasnamen entferne, bekomme ich:

%Vor%

und das ist ziemlich überraschend für mich.

Ein Grund kann in den Ausführungsplänen für die zwei Abfragen gefunden werden; Mit Tabellenaliasen erstellt Oracle einen HASH JOIN und prüft dann auf T1.val = T2.val :

%Vor%

Ohne Aliase wird zuerst ein Vorkommen der Tabelle nach Nicht-Null-Werten gefiltert, wodurch nur eine Zeile ausgewählt wird, und dann wird ein KARTESISCHER mit dem zweiten Vorkommen gebildet, was zwei Zeilen ergibt; auch wenn es korrekt ist, würde ich das Ergebnis eines Cartesian erwarten, aber ich habe keine Zeile mit DESCR = 'NULL VALUE'.

%Vor%

Ist das irgendwie richtig / erwartet? Ist der Ergebniswert des Kartesischen nicht noch seltsamer als die Anzahl der zurückgegebenen Zeilen? Missverstehe ich die Pläne oder vermisse etwas, das so groß ist, dass ich es nicht sehen kann?

    
Aleksej 12.08.2016, 12:20
quelle

4 Antworten

1

Nach Ссылка using(val) wird hier als ON tabnull.val=tabnull.val übersetzt So

%Vor%

Um einen Plan zu erstellen, muss Oracle [virtuell] verschiedene Aliase für jedes JOIN-Mitglied zuweisen, sieht aber keinen Grund, den zweiten Alias ​​an einer beliebigen Stelle in SELECT und ON zu verwenden. Also

%Vor%

Planen

%Vor%     
Serg 12.08.2016 13:45
quelle
0

BEARBEITEN : Ich sage unten, dass die Syntax illegal ist; bei weiterem Nachdenken, das ist BS von meiner Seite, weiß ich das nicht genau (ich kann nicht darauf hinweisen, wo in der Sprachdefinition Aliase für einen Self-Join benötigt werden). Ich glaube immer noch, dass die folgende Erklärung wahrscheinlich richtig ist, sei es für den "Bug" oder für das "undefinierte Verhalten", das ich unten erwähne.

*

Die Syntax ist illegal (Sie wussten das - Sie waren nur neugierig, was passieren würde, und wenn Sie die Ausgabe verstehen können). Ich stimme mit Jarl überein, dass Sie eine Fehlermeldung erhalten haben sollten. Klar, Oracle hat das nicht so programmiert.

Da dies keine gültige Syntax ist, kann das, was Sie sehen, nicht als Fehler bezeichnet werden (daher stimme ich Nicks Kommentar nicht zu). Das Verhalten ist "undefiniert" - wenn Sie eine Syntax verwenden, die nicht von der Oracle-Sprachdefinition unterstützt wird, können Sie irgendwelche verrückten Ergebnisse erhalten, für die Oracle keine Verantwortung übernimmt.

OK, gibt es eine Erklärung für das, was Sie sehen? Ich glaube, dass es in der Tat eine kartesische Verbindung ist, und keine Vereinigung, wie Nick vorgeschlagen hat.

Setzen wir uns in die Schuhe des Optimierers. Es sieht die erste Tabelle in der FROM-Liste, es scannt sie, soweit so gut.

Dann liest es die zweite Tabelle, und es hat eine Liste von Spalten wie folgt:

  

tabNULL.val, tabNULL.descr, tabNULL.val, tabNULL.descr

Die Join-Bedingung ist tabNULL.val = tabNULL.val

Der Optimierer ist dumm, er ist nicht schlau. Anders als Sie erkennen Sie an dieser Stelle nicht, dass tabNULL für zwei verschiedene Inkarnationen des Tisches stehen soll. Er glaubt tabNULL.val auf beiden Seiten der Gleichung ist der gleiche Wert und beide beziehen sich auf die erste "Inkarnation" der Tabelle. Der einzige Fall, in dem dies fehlschlägt, ist, wenn tabNULL.val NULL ist, also REWRITE die Abfrage mit der Klausel wird tabNULL.val IS NOT NULL .

Nur die erste Tabelle wird auf tabNULL.val IS NOT NULL überprüft; der Optimierer "weiß" nicht tabNULL.val erscheint wieder in der Liste und es kann eine UNTERSCHIEDLICHE Bedeutung haben! Dann passiert der Beitritt; An diesem Punkt gibt es keine anderen Bedingungen mehr, so dass BEIDE Zeilen in der zweiten Inkarnation der Tabelle Zeilen im Join für A, ONE CHAR aus der ersten Tabelle erzeugen.

Dann wird in der Projektion wieder nur der FIRST tabNULL.val gelesen und BEIDEN Spalten in der Ausgabe aufgefüllt. Sie fragen die Abfrage-Engine, den Wert tabNULL.val zweimal zurückzugeben, und in Ihrem Kopf ist es von verschiedenen Orten, aber es gibt nur einen Speicherort mit der Bezeichnung tabNULL.val , und es speichert, was aus der ersten Tabelle kam.

Natürlich wissen sehr wenige mit Sicherheit, was der Optimierer und die Abfrage-Engine tun, aber in diesem Fall denke ich, dass dies eine ziemlich sichere Schätzung ist.

    
mathguy 12.08.2016 13:50
quelle
0

Das Schlüsselwort USING ist neu für mich, aber gemäß dem, was ich lese, ist es nur eine neue Art, die SQL-Join-Syntax zu vereinfachen. (Siehe Oracle USING Keyword )

select * from tabNull T1 inner join tabNull T2 using(val);
entspricht:
select * from tabNull T1 inner join tabNull T2 on T1.val = T2.val;

select * from tabNull inner join tabNull using(val);
entspricht:
select * from tabNull inner join tabNull on tabNull.val = tabNull.val;

Das Problem ist, dass in der zweiten Abfrage die Tabellennamen im Join tabNull.val = tabNull.val nicht eindeutig sind Dies ist eine schlechte Syntax, die zu einem Fehler geführt hätte, wenn die traditionelle Joinsyntax verwendet worden wäre Meine beste Vermutung ist, dass Oracle ein vollständiges Cross-Produkt auf den beiden Tabellen durchgeführt hat (das hat alle Zeilen verdoppelt) und dann die Nullen eliminiert, weil USING Equi-Joins verwenden muss (dh gleich " = ") und null ist nichts gleich.

    
user2958463 30.09.2016 07:34
quelle
0

Tut mir leid, ich glaube nicht, dass das wirklich eine Antwort ist. Es ist meist nur ein Kommentar / eine Antwort in Ihrem Beitrag:

  
    

Ist der Ergebniswert des Kartesischen nicht noch seltsamer als die Anzahl der zurückgegebenen Zeilen?

  

Jeder Schritt eines Plans hat eine "Projektion", dh die Liste der Spalten / Ausdrücke, die vom Schritt ausgegeben werden. Was passiert, ist, dass die identischen Aliasnamen bewirken, dass Oracles Projektion zwei Spalten kombiniert, die in nur einer Spalte projiziert werden.

Dies ist einfacher zu sehen, wenn Sie in Ihrem Beispiel zwei separate Tabellen verwenden und ein Paar eindeutig benannte Spalten hinzufügen, um zu sehen, was gerade passiert:

%Vor%

Wie Sie sehen können, war Ihre Theorie über den kartesischen Join korrekt.

    
Matthew McPeak 14.10.2016 17:41
quelle

Tags und Links