Это действительно хороший вопрос.
Сначала я попытался создать таблицу и вставить пример данных (только пять строк):
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);
Я сделал простой тестовый пакет для проверки этого.
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;
/
И тело ...
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;
/
Теперь мы можем запустить тест на Oracle 9i (на 11g такие же результаты):
-- 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);
Результат:
DBMS Output (Session: [1] SCOTT@ORA9i at: 08.09.2010 01:50:04):
-----------------------------------------------------------------------
Count (SELECT) = 3
Count (WHERE) = 5
Вот таблица планов:
--------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost |
--------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | |
|* 1 | TABLE ACCESS FULL | MY_TABLE | | | |
--------------------------------------------------------------------
Это означает, что функция (в значениях WHERE) вызывается для каждой строки таблицы (в случае FULL TABLE SCAN). В операторе SELECT запускается столько раз, сколько соблюдается условие WHERE my_function = 1
Теперь ... протестируйте ваш второй запрос (те же результаты для Oracle9i и 11g)
Результат:
DBMS Output (Session: [1] SCOTT@ORA9i at: 08.09.2010 02:08:04):
-----------------------------------------------------------------------
Count (SELECT) = 8
Count (WHERE) = 0
Объясните обычный вид (для режима оптимизатора CHOOSE):
--------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost |
--------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | |
|* 1 | TABLE ACCESS FULL | MY_TABLE | | | |
--------------------------------------------------------------------
ВОПРОС IS: Почему Count (ВЫБРАТЬ) = 8?
Поскольку Oracle сначала запускает подзапрос (в моем случае с FULL TABLE SCAN, это 5 строк = 5 вызывает my_function в операторе SELECT):
select t.value, my_package.my_function(t.value, 'SELECT') func_value from my_table t
И затем для этого представления (подзапрос похож на представление), запускается 3 раза (из-за условия, когда subquery.func_value = 1) снова вызывает функцию my_function.
Лично я не рекомендую использовать функцию в предложении WHERE, но я допускаю, что иногда это неизбежно.
В качестве наихудшего возможного примера это иллюстрируется следующим:
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');
Где результат в Oracle 9i равен :
Count (SELECT) = 5
Count (WHERE) = 50
А на Oracle 11g есть :
Count (SELECT) = 5
Count (WHERE) = 5
Что в данном случае показывает, что иногда использование функций может иметь решающее значение для производительности. В других случаях (11g) он решает саму базу данных.