It's a really good question.
I first tried create table and insert sample data (five rows only):
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);
I made a simple test package for testing this.
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;
/
And body...
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;
/
Now, we can run test on Oracle 9i (on 11g are same results):
-- 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);
Result is:
DBMS Output (Session: [1] SCOTT@ORA9i at: 08.09.2010 01:50:04):
-----------------------------------------------------------------------
Count (SELECT) = 3
Count (WHERE) = 5
Here is plan table:
--------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost |
--------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | |
|* 1 | TABLE ACCESS FULL | MY_TABLE | | | |
--------------------------------------------------------------------
Which means that the function (in WHERE calues) is called for every row of the table (in the case of FULL TABLE SCAN). In the SELECT statement is launched just as many times comply with condition WHERE my_function = 1
Now... test your second query (same results on Oracle9i and 11g)
Result is:
DBMS Output (Session: [1] SCOTT@ORA9i at: 08.09.2010 02:08:04):
-----------------------------------------------------------------------
Count (SELECT) = 8
Count (WHERE) = 0
Explain plain look like this (for CHOOSE optimizer mode):
--------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost |
--------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | |
|* 1 | TABLE ACCESS FULL | MY_TABLE | | | |
--------------------------------------------------------------------
QUESTION IS: Why Count (SELECT) = 8?
Because Oracle first run subquery (in my case with FULL TABLE SCAN, it's 5 rows = 5 calls my_function in SELECT statement):
select t.value, my_package.my_function(t.value, 'SELECT') func_value from my_table t
And than for this view (subquery is like view) run 3 times (due to the condition where subquery.func_value = 1) again call function my_function.
Personally not recommend to use function in the WHERE clause, but I admit that sometimes this is unavoidable.
As the worst possible example of this is illustrated by the following:
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');
Where the result on Oracle 9i is:
Count (SELECT) = 5
Count (WHERE) = 50
And on Oracle 11g is:
Count (SELECT) = 5
Count (WHERE) = 5
Which in this case shows that sometimes the use of functions may be critical for performance. In other cases (11g) it solves the database itself.
fv
column in an output field) - it stays back on five. I thought oracle just propagates subquery-level function call result to an upper level, and it does, but in quite a weird way.where t1.fv = 1
doesn't call a function again, but listingt1.fv
in a list of selected columns does. – Toneless