2016-06-15 6 views
3

Dies ist langsam:Wie Oracle-Hinweise oder andere Optimierung zu verwenden, um Funktion in Where-Klausel-Leistungsproblem zu beheben?

select col_x from table_a where col_y in (select col_y from table_b) and fn(col_x)=0; 

Aber ich weiß, dass diese schnelle 4 Zeilen zurück, und dass ich schnell fn() auf 4 Werte laufen.

So mache ich einige Tests, und ich sehe, dass dies schnell:

select fn(col_x) from table_a where col_y in (select col_y from table_b); 

Wenn die fn() verwendet in der where-Klausel wird Oracle es in TABLE_A auf jeder Zeile ausgeführt wird. Wie kann ich es so machen, dass Oracle zuerst den col_y-Filter verwendet und nur die Funktion für die übereinstimmenden Zeilen ausführt?

Zum Beispiel konzeptionell, obwohl ich dies funktionieren würde:

with taba as (
    select fn(col_x) x from table_a where col_y in (select col_y from table_b) 
) 
select * from taba where x=0; 

weil ich dachte, Oracle die Klausel zunächst laufen würde, aber Oracle „Optimierung“ Diese Abfrage und machen diesen Lauf genau das gleiche wie die erste Abfrage oben, wobei fn (col_x) = 0 in der where-Klausel steht.

Ich möchte, dass dies nur als Abfrage und nicht in einem PL/SQL-Block ausgeführt wird. Es scheint, als sollte es einen Weg geben, Orakel einen Hinweis zu geben oder einen anderen Trick zu machen, aber ich kann es nicht herausfinden. BTW, Tabelle ist auf col_y indiziert und wird als Zugriffsprädikat verwendet. Statistiken sind aktuell.

+0

Welche Funktion hat 'fn'? Wenn es mehrmals mit demselben Parameter aufgerufen wird, gibt es immer den gleichen Wert zurück? Wenn ja, können Sie die Funktion als deterministisch definieren und dann einen funktionsbasierten Index darauf erstellen? – sstan

+0

Hallo sstan, nein, es ist nicht deterministisch. Es ist eine komplexe Funktion und hat einen Cursor darin. –

Antwort

2

Es gibt zwei Möglichkeiten, die Sie es gehen könnte,

1) in 'AND rownum> = 0' in der Unterabfrage Materialisierung zu erzwingen.

OR

2) verwenden, um eine Case-Anweisung in der Abfrage die Ausführungspriorität zu erzwingen (vielleicht)

+0

Hallo Arcan3, Danke für deine Antwort. # 1 funktioniert. Ich habe Nr. 2 nicht versucht, da ich nicht sicher war, wie ich es umsetzen sollte. –

0

Dies funktioniert, aber wenn jemand eine bessere Antwort hat, bitte teilen:

select col_x 
from table_a 
where col_y in (select col_y from table_b) 
    and (select 1 from dual where fn(col_x)=0); 

Art kludgy, aber funktioniert. Führt eine Abfrage aus, die in 60+ Sekunden bis zu 0,1 Sekunden ausgeführt wird.

0

Sie könnten versuchen, die Klausel in Ihrer Abfrage. Diese Klausel wird erst ausgeführt, wenn die Basisabfrage abgeschlossen ist, und dann wird die HAVING-Klausel für die resultierenden Zeilen ausgeführt. Es wird normalerweise für analytische Funktionen verwendet, könnte aber in Ihrem Fall nützlich sein.

select col_x 
from table_a 
where col_y in (select col_y from table_b) 
having fn(col_x)=0; 

A HAVING-Klausel schränkt die Ergebnisse einer GROUP BY in einer SelectExpression. Die HAVING-Klausel wird auf jede Gruppe der gruppierten Tabelle angewendet, ähnlich wie eine WHERE-Klausel auf eine Auswahlliste angewendet wird. Wenn keine GROUP BY-Klausel ist, wird die HAVING-Klausel auf das gesamte Ergebnis als einzelne Gruppe angewendet. Die SELECT-Klausel kann nicht direkt auf eine Spalte verweisen, die keine GROUP BY-Klausel enthält. Es kann jedoch auf Konstanten, Aggregate und spezielle Register verweisen.

http://docs.oracle.com/javadb/10.8.3.0/ref/rrefsqlj14854.html

+0

Ich habe das getestet, und es funktioniert auch gut. Es erfordert auch eine "group by col_x", also ist es nicht super sauber (besonders mit meiner tatsächlichen Tabelle, die viele Spalten hat), aber es ist eine kluge Idee. –

+0

@BobThule Ja, laut den Dokumenten ist eine Gruppe von nicht erforderlich, aber nach meiner Erfahrung ist es manchmal erforderlich. Ich habe die Regeln nicht genau herausgefunden, wann es erforderlich ist oder nicht - hängt wahrscheinlich davon ab, wie die Abfrage strukturiert ist. – Joel

0

1) Warum versuchen Sie nicht beitreten TABLE_A und Table_B col_y verwenden.

select a.col_x from table_a a,table_b b 
    where a.col_y = b.col_y 
    and fn(col_x) = 0 

2) NO_PUSH_PRED -

select /*+ NO_PUSH_PRED(v) */ col_x from (
    select col_x from table_a where col_y in (select col_y from table_b) 
    ) v 
    where fn(col_x) =0 

3) vorhanden ist, und PUSH_SUBQ.

select col_x from table_a a 
    where exists(select /*+ PUSH_SUBQ */ 1 from table_b b where a.col_y = b.coly) 
    and fn(col_x) = 0; 
+0

Hallo Arkadiusz, Danke für Ihre Antwort. 1) Mein Beispiel versuchte nur zu zeigen, dass ich ein Zugriffsprädikat für col_y hatte. Abhängig von der Abfrage würde ich einen Join machen, aber ich würde immer ANSI-Joinsyntax bevorzugen. 2) Ich war begeistert, weil es die Art von Antwort war, nach der ich suchte - aber es lief die Anfrage immer noch sehr langsam ab. 3) Leider war mein Beispiel eine erfundene Version einer komplexeren Abfrage, und ich kann dies nicht in die komplexe Abfrage übersetzen. –

Verwandte Themen