Het is echt een goede vraag.
Ik heb eerst geprobeerd een tabel te maken en voorbeeldgegevens in te voegen (slechts vijf rijen):
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);
Ik heb een eenvoudig testpakket gemaakt om dit te 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;
/
En lichaam...
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;
/
Nu kunnen we testen uitvoeren op Oracle 9i (op 11g zijn dezelfde resultaten):
-- 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);
Resultaat is:
DBMS Output (Session: [1] [email protected] at: 08.09.2010 01:50:04):
-----------------------------------------------------------------------
Count (SELECT) = 3
Count (WHERE) = 5
Hier is de plantabel:
--------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost |
--------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | |
|* 1 | TABLE ACCESS FULL | MY_TABLE | | | |
--------------------------------------------------------------------
Dat betekent dat de functie (in WHERE-calues) wordt aangeroepen voor elke rij van de tabel (in het geval van FULL TABLE SCAN). In de SELECT-instructie wordt net zo vaak gestart als voldoen aan voorwaarde WHERE my_function =1
Nu... test uw tweede zoekopdracht (dezelfde resultaten op Oracle9i en 11g)
Resultaat is:
DBMS Output (Session: [1] [email protected] at: 08.09.2010 02:08:04):
-----------------------------------------------------------------------
Count (SELECT) = 8
Count (WHERE) = 0
Leg uit dat het er zo uitziet (voor CHOOSE optimizer-modus):
--------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost |
--------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | |
|* 1 | TABLE ACCESS FULL | MY_TABLE | | | |
--------------------------------------------------------------------
VRAAG IS:Waarom tellen (SELECT) =8?
Omdat Oracle eerst een subquery uitvoert (in mijn geval met FULL TABLE SCAN zijn het 5 rijen =5 roept my_function aan in de SELECT-instructie):
select t.value, my_package.my_function(t.value, 'SELECT') func_value from my_table t
En dan voor deze weergave (subquery is als weergave) 3 keer uitvoeren (vanwege de voorwaarde waarbij subquery.func_value =1) opnieuw de functie my_function aanroept.
Persoonlijk raad ik het niet aan om de functie in de WHERE-clausule te gebruiken, maar ik geef toe dat dit soms onvermijdelijk is.
Het slechtst mogelijke voorbeeld hiervan wordt geïllustreerd door het volgende:
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');
Waar het resultaat op Oracle 9i is :
Count (SELECT) = 5
Count (WHERE) = 50
En op Oracle 11g is :
Count (SELECT) = 5
Count (WHERE) = 5
Wat in dit geval laat zien dat het gebruik van functies soms van cruciaal belang kan zijn voor de prestaties. In andere gevallen (11g) lost het de database zelf op.