Oracle
 sql >> Database >  >> RDS >> Oracle

In che modo Oracle elabora le chiamate di funzione memorizzate in SQL?

È davvero un'ottima domanda.

Per prima cosa ho provato a creare una tabella e inserire dati di esempio (solo cinque righe):

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);

Ho creato un semplice pacchetto di test per testare questo.

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;
/

E il corpo...

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;
/

Ora possiamo eseguire il test su Oracle 9i (su 11g sono gli stessi risultati):

-- 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);

Il risultato è:

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

Ecco la tabella del piano:

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

Ciò significa che la funzione (in WHERE calues) viene chiamata per ogni riga della tabella (nel caso di FULL TABLE SCAN). Nell'istruzione SELECT viene lanciata altrettante volte conforme alla condizione WHERE my_function =1

Ora... prova la tua seconda query (stessi risultati su Oracle9i e 11g)

Il risultato è:

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

Spiega in modo semplice come segue (per SCEGLI la modalità ottimizzatore):

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

LA DOMANDA È:Perché contare (SELECT) =8?

Poiché Oracle esegue prima la sottoquery (nel mio caso con FULL TABLE SCAN, sono 5 righe =5 chiamate my_function nell'istruzione SELECT):

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

E poi per questa vista (la sottoquery è come la vista) esegui 3 volte (a causa della condizione in cui subquery.func_value =1) chiama di nuovo la funzione mia_funzione.

Personalmente sconsiglio di utilizzare la funzione nella clausola WHERE, ma ammetto che a volte questo è inevitabile.

Come il peggior esempio possibile di ciò è illustrato da quanto segue:

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');

Dove è il risultato su Oracle 9i :

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

E su Oracle 11g lo è :

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

Il che in questo caso mostra che a volte l'uso delle funzioni può essere critico per le prestazioni. Negli altri casi (11g) risolve il database stesso.