Oracle
 sql >> Datenbank >  >> RDS >> Oracle

Wie verarbeitet Oracle gespeicherte Funktionsaufrufe in SQL?

Das ist eine wirklich gute Frage.

Ich habe zuerst versucht, eine Tabelle zu erstellen und Beispieldaten einzufügen (nur fünf Zeilen):

create table my_table(value number);
insert into my_table(value) values(1);
insert into my_table(value) values(2);
insert into my_table(value) values(3);
insert into my_table(value) values(4);
insert into my_table(value) values(5);

Ich habe ein einfaches Testpaket erstellt, um dies zu testen.

create or replace package my_package is
  g_counter_SELECT PLS_INTEGER := 0; -- counter for SELECT statement
  g_counter_WHERE  PLS_INTEGER := 0; -- counter for WHERE clause
  function my_function(number_in in number, type_in in varchar2) return number;
  procedure reset_counter;
end;
/

Und Körper...

create or replace package body my_package is
  function my_function(number_in in number, type_in in varchar2) return number is
  begin
    IF(type_in = 'SELECT') THEN
        g_counter_SELECT := g_counter_SELECT + 1;
    ELSIF(type_in = 'WHERE') THEN
        g_counter_WHERE := g_counter_WHERE + 1;
    END IF;
    return mod(number_in, 2);
  end;
  procedure reset_counter is
  begin
    g_counter_SELECT := 0;
    g_counter_WHERE := 0;
  end;
end;
/

Jetzt können wir den Test auf Oracle 9i ausführen (auf 11g sind die gleichen Ergebnisse):

-- reset counter
exec my_package.reset_counter();

-- run query
select t.value, my_package.my_function(t.value, 'SELECT')
  from my_table t
 where my_package.my_function(t.value, 'WHERE') = 1;

-- print result
exec dbms_output.put_line('Count (SELECT) = ' || my_package.g_counter_SELECT);
exec dbms_output.put_line('Count (WHERE) = ' || my_package.g_counter_WHERE);

Ergebnis ist:

DBMS Output (Session: [1] [email protected] at: 08.09.2010 01:50:04): 
-----------------------------------------------------------------------
Count (SELECT) = 3
Count (WHERE) = 5

Hier ist die Plantabelle:

--------------------------------------------------------------------
| Id  | Operation            |  Name       | Rows  | Bytes | Cost  |
--------------------------------------------------------------------
|   0 | SELECT STATEMENT     |             |       |       |       |
|*  1 |  TABLE ACCESS FULL   | MY_TABLE    |       |       |       |
--------------------------------------------------------------------

Das bedeutet, dass die Funktion (in WHERE-Werten) für jede Zeile der Tabelle aufgerufen wird (im Fall von FULL TABLE SCAN). In der SELECT-Anweisung wird genauso oft die Bedingung erfüllt WHERE my_function =1

gestartet

Jetzt... testen Sie Ihre zweite Abfrage (gleiche Ergebnisse auf Oracle9i und 11g)

Ergebnis ist:

DBMS Output (Session: [1] [email protected] at: 08.09.2010 02:08:04): 
-----------------------------------------------------------------------
Count (SELECT) = 8
Count (WHERE) = 0

Erklären Sie das einfache Aussehen wie folgt (für den Optimierungsmodus WÄHLEN):

--------------------------------------------------------------------
| Id  | Operation            |  Name       | Rows  | Bytes | Cost  |
--------------------------------------------------------------------
|   0 | SELECT STATEMENT     |             |       |       |       |
|*  1 |  TABLE ACCESS FULL   | MY_TABLE    |       |       |       |
--------------------------------------------------------------------

FRAGE IST:Warum zählen (SELECT) =8?

Weil Oracle zuerst eine Unterabfrage ausführt (in meinem Fall mit FULL TABLE SCAN sind es 5 Zeilen =5 Aufrufe my_function in der SELECT-Anweisung):

select t.value, my_package.my_function(t.value, 'SELECT') func_value from my_table t

Und dann für diese Ansicht (Unterabfrage ist wie Ansicht) 3 Mal ausführen (aufgrund der Bedingung wo subquery.func_value =1) erneut die Funktion my_function aufrufen.

Ich persönlich empfehle nicht, function in der WHERE-Klausel zu verwenden, aber ich gebe zu, dass dies manchmal unvermeidlich ist.

Als schlimmstmögliches Beispiel wird dies durch Folgendes veranschaulicht:

select t.value, my_package.my_function(t.value, 'SELECT')
  from my_table t
 where my_package.my_function(t.value, 'WHERE') = my_package.my_function(t.value, 'WHERE')
   and my_package.my_function(t.value, 'WHERE') = my_package.my_function(t.value, 'WHERE')
   and my_package.my_function(t.value, 'WHERE') = my_package.my_function(t.value, 'WHERE')
   and my_package.my_function(t.value, 'WHERE') = my_package.my_function(t.value, 'WHERE')
   and my_package.my_function(t.value, 'WHERE') = my_package.my_function(t.value, 'WHERE');

Wo das Ergebnis auf Oracle 9i ist :

Count (SELECT) = 5
Count (WHERE) = 50

Und auf Oracle 11g ist :

Count (SELECT) = 5
Count (WHERE) = 5

Was in diesem Fall zeigt, dass die Verwendung von Funktionen manchmal performancekritisch sein kann. In anderen Fällen (11g) löst es die Datenbank selbst auf.