2012-04-10 13 views
5

Dumb Fragestunde. Oracle 10g.Where-Klausel, die den Join betrifft

Kann eine where-Klausel einen Join beeinflussen?

Ich habe eine Abfrage in Form bekommt:

select * from 
(select product, product_name from products p 
join product_serial ps on product.id = ps.id 
join product_data pd on pd.product_value = to_number(p.product_value)) product_result 
where product_name like '%prototype%'; 

Offensichtlich ist dies ein konstruiertes Beispiel ist. Keine wirkliche Notwendigkeit, die Tabellenstruktur zu zeigen, da alles imaginär ist. Leider kann ich die tatsächliche Tabellenstruktur oder Abfrage nicht anzeigen. In diesem Fall ist p.product_value ein VARCHAR2-Feld, in dem in bestimmten Zeilen eine ID gespeichert ist und nicht Text. (Ja, schlechtes Design - aber etwas, das ich geerbt habe und nicht ändern kann)

Das Problem ist in der Join. Wenn ich die where-Klausel weglasse, funktioniert die Abfrage und Zeilen werden zurückgegeben. Wenn ich jedoch die WHERE-Klausel hinzufüge, erhalte ich den Fehler "ungültige Zahl" in der Join-Bedingung pd.product_value = to_number (p.product_value).

Offensichtlich tritt der Fehler "ungültige Zahl" auf, wenn Zeilen verbunden werden, die Nicht-Ziffern im Feld p.product_value enthalten. Meine Frage ist jedoch, wie werden diese Zeilen ausgewählt? Wenn der Join ohne die äußere WHERE-Klausel erfolgreich ist, sollte nicht die äußere WHERE-Klausel nur Zeilen aus dem Ergebnis des Joins auswählen? Es scheint, dass die WHERE-Klausel Auswirkungen darauf hat, welche Zeilen verbunden sind, obwohl die Verknüpfung in einer inneren Abfrage enthalten ist.

Ist meine Frage sinnvoll?

+0

Sie könnten es ein wenig locker geben 'to_char (pd.product_value) = p.product_value'. – briantyler

Antwort

1

Kurze Antwort: ja.

Lange Antwort: die Abfrage-Engine ist frei, Ihre Abfrage neu zu schreiben, wie es will, solange es die gleichen Ergebnisse zurückgibt. Die gesamte Abfrage steht ihr zur Verfügung, um sie für die effizienteste Abfrage zu verwenden.

In diesem Fall würde ich vermuten, dass es einen Index gibt, der deckt, was Sie wollen, aber es deckt Produktname nicht ab, wenn Sie das zur where-Klausel hinzufügen, wird der Index nicht verwendet und stattdessen Es gibt einen Scan, bei dem beide Bedingungen gleichzeitig getestet werden, also Ihr Fehler.

Was wirklich ein Fehler in Ihrer Join-Bedingung ist, sollten Sie nicht zu to_number verwenden, es sei denn, Sie sind sicher, dass es eine Nummer ist.

0

Ich denke, Ihre to_number(p.product_value) gilt nur für Zeilen mit einer gültigen product_name.

Was passiert ist, dass Ihre join vor Ihrer where Klausel angewendet wird, was zum Ausfall der to_number Funktion führt.

Was Sie tun müssen, ist Ihre product_name like '%prototype%' als JOIN Klausel wie folgt umfassen:

select * from 
(select product, product_name from products p 
join product_serial ps on product.id = ps.id 
join product_data pd on product_name like '%prototype%' AND 
    pd.product_value = to_number(p.product_value)); 
+0

Dies kann oder kann nicht helfen - es gibt nichts, was Oracle davon abhält, die 'to_number' zuerst auszuwerten. –

2

Es wirkt sich auf den Plan, die generiert wird.

Die tatsächliche Reihenfolge, in der Tabellen verknüpft (und gefiltert) werden, wird nicht von der Reihenfolge bestimmt, in der Sie Ihre Abfrage schreiben, sondern von der Statistik der Tabellen.

In einer Version generiert der co-zufällig erzeugte Plan, dass die "schlechten" Zeilen nie verarbeitet werden; weil die vorhergehenden Joins die Ergebnismenge auf einen Punkt heruntergefiltert haben, zu dem sie nie hinzugefügt wurden.

Die Einführung der WHERE-Klausel hat dazu geführt, dass ORACLE jetzt eine andere Join-Reihenfolge für besser hält (da die Filterung nach dem Produktnamen einen bestimmten Index erfordert oder die Daten stark einschränkt usw.).

Diese neue Reihenfolge bedeutet, dass die 'schlechten' Zeilen vor dem Join verarbeitet werden, der sie ausfiltert.


Ich würde versuchen, die Daten zu reinigen, bevor sie abfragt. Möglicherweise durch Erstellen einer abgeleiteten Spalte, in der der Wert bereits in eine Zahl umgewandelt wurde, oder als NULL, wenn dies nicht möglich ist.

Sie können auch EXPLAIN PLAN verwenden, um zu sehen, dass die verschiedenen Pläne von Ihren Abfragen abweichen.

0

Für mehr Hintergrund (und eine wirklich gute lesen), würde ich vorschlagen, Jonathan Gennicks Subquery Madness zu lesen.

Grundsätzlich besteht das Problem darin, dass Oracle Prädikate in beliebiger Reihenfolge bewerten kann. Es ist also frei, das Prädikat product_name in Ihre Unterabfrage zu schieben (oder nicht zu drücken). Es ist kostenlos, die Join-Bedingungen in beliebiger Reihenfolge zu bewerten. Wenn Oracle also einen Abfrageplan auswählt, in dem die nicht numerischen product_value Zeilen herausgefiltert werden, bevor es die to_number anwendet, wird die Abfrage erfolgreich ausgeführt. Wenn es passiert, einen Plan auszuwählen, wo es die to_number vor dem Ausfiltern der nicht numerischen product_value Zeilen anwendet, erhalten Sie einen Fehler. Natürlich ist es auch möglich, dass es die ersten N Zeilen erfolgreich zurückgibt, und dann erhalten Sie einen Fehler, wenn Sie versuchen, Zeile N + 1 abzurufen, da Zeile N + 1 das erste Mal versucht, das to_number Prädikat anzuwenden zu einem nicht-numerischen Daten. Wenn Sie das Datenmodell nicht reparieren, können Sie möglicherweise einige Hinweise in die Abfrage einfügen, um Oracle dazu zu zwingen, das Prädikat auszuwerten, das sicherstellt, dass alle nicht numerischen Daten herausgefiltert werden, bevor das Prädikat to_number angewendet wird. Aber im Allgemeinen ist es etwas schwierig, eine Abfrage so anzudeuten, dass der Optimierer die Dinge immer in der "richtigen" Reihenfolge auswertet.

Verwandte Themen