1

PL/SQL 関数内で結合を行うために、オブジェクトに対して table() 関数を使用しようとしています。この関数を使用すると、クエリが完了するまでに最大 20 分かかる場合があります。代わりにテーブルにデータを直接入力すると、5 秒もかかりません。このような大きな違いがある理由はわかりませんが、結合テーブルの列のインデックスが使用されていないというのが私の推測です。テーブルとオブジェクトの列定義は同じです。

コード例を次に示します。

create or replace type VARCHAR20_TYPE is OBJECT
(
  val varchar2(20 byte);
);

create or replace type VARCHAR20_TABLE is table of VARCHAR20_TYPE;


create or replace FUNCTION test_function( 
    in_project_ids VARCHAR20_TABLE
  ) RETURN INTEGER
  IS
    l_result INTEGER;
  BEGIN

    SELECT count(*) into l_result FROM project p JOIN TABLE(in_project_ids) t ON p.project_id = t.val;      
    RETURN l_result;

  END;

in_project_ids上記の例を同じ列定義を持つ実際のテーブルへの結合に置き換えると、関数のパフォーマンスが大幅に向上します。

4

1 に答える 1

4

これは予想されることです。このようなメモリ配列を扱う場合、Oracleは8k行がそのテーブルにあると想定します。

それを助けるためにこれを試してください:

SELECT /*+ cardinality(t, 20) */ count(*) into l_result FROM project p JOIN TABLE(in_project_ids) t ON p.project_id = t.val;

ここで、20 は実際のエントリ数の大まかな推測です。これは、ヒンティングが「OK」である (オプティマイザを支援するために必要な) エッジ ケースの 1 つです。

編集

例えば:

SQL> explain plan for SELECT /*+ cardinality(t, 1) */ * FROM project p JOIN TABLE(VARCHAR20_TABLE()) t ON p.project_id = t.val;

Explained.

SQL> select * From table(dbms_xplan.display);

Plan hash value: 858605789

--------------------------------------------------------------------------------------------------------
| Id  | Operation                               | Name         | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                        |              |     1 |    27 |    30   (0)| 00:00:01 |
|   1 |  NESTED LOOPS                           |              |       |       |            |          |
|   2 |   NESTED LOOPS                          |              |     1 |    27 |    30   (0)| 00:00:01 |
|   3 |    COLLECTION ITERATOR CONSTRUCTOR FETCH|              |     1 |     2 |    29   (0)| 00:00:01 |
|*  4 |    INDEX UNIQUE SCAN                    | SYS_C0011177 |     1 |       |     0   (0)| 00:00:01 |
|   5 |   TABLE ACCESS BY INDEX ROWID           | PROJECT      |     1 |    25 |     1   (0)| 00:00:01 |
--------------------------------------------------------------------------------------------------------


Predicate Information (identified by operation id):
---------------------------------------------------

   4 - access("P"."PROJECT_ID"=TO_NUMBER(SYS_OP_ATG(VALUE(KOKBF$),1,2,2)))

Note
-----
   - dynamic sampling used for this statement (level=2)

21 rows selected.

SQL> explain plan for SELECT * FROM project p JOIN TABLE(VARCHAR20_TABLE()) t ON p.project_id = t.val;

Explained.

SQL> select * From table(dbms_xplan.display);

Plan hash value: 583089723

--------------------------------------------------------------------------------------------------
| Id  | Operation                              | Name    | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                       |         |  8168 |   215K|    33   (4)| 00:00:01 |
|*  1 |  HASH JOIN                             |         |  8168 |   215K|    33   (4)| 00:00:01 |
|   2 |   TABLE ACCESS FULL                    | PROJECT |  2000 | 50000 |     3   (0)| 00:00:01 |
|   3 |   COLLECTION ITERATOR CONSTRUCTOR FETCH|         |  8168 | 16336 |    29   (0)| 00:00:01 |
--------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------


   1 - access("P"."PROJECT_ID"=TO_NUMBER(SYS_OP_ATG(VALUE(KOKBF$),1,2,2)))

Note
-----
   - dynamic sampling used for this statement (level=2)

19 rows selected.

些細な例ですが、ヒントなしでコレクション fetch = 8168 の「行」と結果としての計画の変更に注意してください。実際のテーブルとコレクションとヒント付きコレクションを使用して説明計画を確認し、適切なカーディナリティ ヒント番号を使用して、計画とパフォーマンスを改善する必要があります。

于 2012-11-28T21:24:41.713 に答える