3

彼ら。たとえば、クエリがあります。

select t.value, my_stored_function(t.value)
  from my_table t
 where my_stored_function(t.value) = n_Some_Required_Value

私はそれを次のように書き直しました:

select value, func_value
  from (select t.value, my_stored_function(t.value) func_value 
          from my_table t) subquery
 where subquery.func_value = n_Some_Required_Value

my_stored_functionリソースを消費するものとして考えてみましょう。2番目のクエリでは、呼び出される回数が2回少なくなると思いますが、この変更後、パフォーマンスが大幅に向上することはありませんでした。

だから、私の仮定は間違っていたと思います。では、Oracleは実際にこれらの関数呼び出しをどのように処理しますか?

4

4 に答える 4

4

本当に良い質問です。

最初にテーブルを作成してサンプルデータを挿入しようとしました(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] 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 calues)がテーブルのすべての行に対して呼び出されることを意味します(FULL TABLE SCANの場合)。SELECTステートメントでは、条件WHERE my_function=1に準拠する回数だけ起動されます。

次に...2番目のクエリをテストします(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    |       |       |       |
--------------------------------------------------------------------

質問は:なぜカウント(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

そして、このビュー(サブクエリはビューに似ています)よりも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');

Oracle9iでの結果は次のとおりです。

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

そして、Oracle11gでは

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

この場合、これは、関数の使用がパフォーマンスにとって重要な場合があることを示しています。その他の場合(11g)は、データベース自体を解決します。

于 2010-09-08T00:29:59.910 に答える
1

どちらの場合も、関数はのすべての行に対して1回呼び出されmy_tableます。最初のケースでは、呼び出しはwhere句の結果であり、見つかったばかりの値は再度計算されることなく返されます。2番目のケースでは、計算されたすべての値がサブクエリから返され、外部クエリのwhere句によってフィルタリングされます。

編集:マーティンのテストに基づくと、明らかに真実ではありません。今、私は戻って、何年も前に行ったテストを見つけて、これが事実であると思い、何が間違っていたかを確認する必要があります。FBIについてのビットはまだ真実です。私は願います。

メモリ使用量とオプティマイザが使用する正確なプランに若干の違いがあるかもしれませんが、どちらも重要だとは思いませんでした。ほぼ確実に、関数呼び出し自体のコストに反することはありません。

これを最適化するために私が見ることができる唯一の方法は、関数ベースのインデックスを使用することです。

于 2010-09-07T09:06:52.517 に答える
0

PL / SQLプラグマを使用して、Oracleがクエリを最適化する方法に影響を与えることができます。RESTRICT_REFERENCESプラグマを参照してください

于 2010-09-08T08:38:12.277 に答える
0

簡単なテスト:

create or replace function print_function(v1 number) return number is
begin
   dbms_output.put_line(v1);
   return v1;
end;
/

select print_function(ASCII(dummy)) as test
  from dual
 where chr(print_function(ASCII(dummy))) = dummy;

結果(10gを使用):

      TEST
----------
        88

88
88

結論:関数はSELECT句とWHERE句で別々に実行されました。

于 2010-09-07T17:51:44.237 に答える