それは本当に良い質問です。
最初にテーブルを作成してサンプルデータを挿入しようとしました(5行のみ):
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] [email protected] 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 calues)がテーブルのすべての行に対して呼び出されることを意味します(FULL TABLE SCANの場合)。 SELECTステートメントでは、条件WHERE my_function =1
に準拠する回数だけ起動されます。次に...2番目のクエリをテストします (Oracle9iと11gで同じ結果)
結果は次のとおりです:
DBMS Output (Session: [1] [email protected] 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 | | | |
--------------------------------------------------------------------
質問は次のとおりです。カウント(SELECT)=8を選択する理由
Oracleは最初にサブクエリを実行するため(私の場合はFULL TABLE SCANの場合、5行=SELECTステートメントでmy_functionを5回呼び出します):
select t.value, my_package.my_function(t.value, 'SELECT') func_value from my_table t
そして、このビュー(サブクエリはビューに似ています)よりも(subquery.func_value =1の条件のために)3回実行し、関数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');
Oracle9iでの結果はどこにありますか :
Count (SELECT) = 5
Count (WHERE) = 50
Oracle11gでは :
Count (SELECT) = 5
Count (WHERE) = 5
この場合、これは、関数の使用がパフォーマンスにとって重要な場合があることを示しています。その他の場合(11g)は、データベース自体を解決します。