3

テーブル関数を使用してexists句でコレクションを使用して、Oracleでプランを生成する方法

例えば

CREATE TYPE TYP_EMP AS OBJECT(
EMPNO     NUMBER(4),
ENAME     VARCHAR2(10),
JOB       VARCHAR2(9),
MGR       NUMBER(4),
HIREDATE  DATE,
SAL       NUMBER(7,2),
COMM      NUMBER(7,2),
DEPTNO    NUMBER(2));
/

タイプが作成されました。

CREATE TYPE TYP_EMP_COLL AS TABLE OF TYP_EMP;
/

タイプが作成されました。

CREATE OR REPLACE VIEW VW_EMP 
OF TYP_EMP
WITH OBJECT IDENTIFIER(EMPNO) AS
SELECT
EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM, DEPTNO
FROM EMP
/

作成されたビュー

CREATE TYPE TYP_DEPT AS OBJECT(
DEPTNO NUMBER(2),
DNAME  VARCHAR2(14),
LOC    VARCHAR2(13));
/

作成されたタイプ

CREATE TYPE TYP_DEPT_COLL AS TABLE OF TYP_DEPT;
/

作成されたタイプ

CREATE OR REPLACE VIEW VW_DEPT
OF TYP_DEPT
WITH OBJECT IDENTIFIER(DEPTNO) AS
SELECT
DEPTNO, DNAME, LOC
FROM DEPT
/

CREATE OR REPLACE PROCEDURE SP_EMPTEST AS
V_EMP TYP_EMP_COLL;
V_DEPT TYP_DEPT_COLL;
BEGIN
SELECT VALUE(V) BULK COLLECT INTO V_EMP FROM VW_EMP V;
SELECT VALUE(VD)
BULK COLLECT INTO V_DEPT FROM VW_DEPT VD
WHERE EXISTS
    (SELECT 1 FROM TABLE(V_EMP) VV WHERE VD.DEPTNO = VV.DEPTNO);
END;
/

トレースファイルの TKPROF 出力

********************************************************************************

SQL ID: 7c02yjs9q5kqr
Plan Hash: 2616009478
SELECT VALUE(VD) 
FROM
VW_DEPT VD WHERE EXISTS (SELECT 1 FROM TABLE(:B1 ) VV WHERE VD.DEPTNO = VV.DEPTNO)


call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.00       0.00          0          0          0           0
Execute      1      0.00       0.00          0         19          0           0
Fetch        1      0.00       0.00          0          7          0           3
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total        3      0.00       0.00          0         26          0           3

Misses in library cache during parse: 1
Misses in library cache during execute: 1
Optimizer mode: ALL_ROWS
Parsing user id: 84  (mac)   (recursive depth: 1)

Rows     Row Source Operation
-------  ---------------------------------------------------
  3  HASH JOIN SEMI (cr=7 pr=0 pw=0 time=6 us cost=33 size=32 card=1)
  4   TABLE ACCESS FULL DEPT (cr=7 pr=0 pw=0 time=0 us cost=3 size=120 card=4)
 14   COLLECTION ITERATOR PICKLER FETCH (cr=0 pr=0 pw=0 time=0 us cost=29 size=28 card=14)

error during execute of EXPLAIN PLAN statement
ORA-22905: cannot access rows from a non-nested table item

parse error offset: 129

Elapsed times include waiting on following events:
Event waited on                             Times   Max. Wait  Total Waited
----------------------------------------   Waited  ----------  ------------
asynch descriptor resize                        1        0.00          0.00
********************************************************************************
4

1 に答える 1

4

説明計画を取得するには、さまざまな方法があります。これは単なる網羅的なリストではありません。これらの方法には重要な違いがあると思います。それぞれの方法をツール キットに含める価値があります。

1 - プロシージャーへの呼び出しをトレースします。

エラーが示すように、SQL を PL/SQL ブロックから引き出すのは難しい場合があります。PL/SQL と SQL は連携して動作するように設計されていますが、それらの間の相互作用が奇妙な場合があります。

手順をトレースすると、確実にコンテキストが正しくなり、進行中の他の重要なことも示される場合があります。私は一般的にトレースを避けていますが、これは役に立たない情報が大量に作成され、サーバーへのアクセスが通常 (不可解なことに) 難しいためです。

2 - SQL で型をインスタンス化する

空のコレクションを作成すると、通常の説明プラン メソッドが機能するはずです。

SELECT VALUE(VD) 
FROM
VW_DEPT VD WHERE EXISTS (SELECT 1 FROM TABLE(typ_emp_coll()  ) VV WHERE VD.DEPTNO = VV.DEPTNO);

または、実際の値を入力します。

SELECT VALUE(VD) 
FROM
VW_DEPT VD WHERE EXISTS (SELECT 1 FROM TABLE(typ_emp_coll(typ_emp(null,null,null,null,null,null,null,null))  ) VV WHERE VD.DEPTNO = VV.DEPTNO);

これに意味のある値を入力するには、大規模な SQL ステートメントが必要になる場合があります。しかし、すべてを 1 か所にまとめておくと便利な場合もあります。また、後で説明しますが、実際には空のコレクションを使用しても問題ありません。Oracle は、空のコレクションと巨大なコレクションを区別できません。

3 - 実行されたクエリの SQL_ID を使用します。

で SQL_ID を見つけますV$SQL。これにより複数の行が返される場合があり、正しい行を手動で選択する必要がある場合があります。

select * from v$sql where upper(sql_text) like '%SELECT 1 FROM TABLE(%';

sql_id は、次のような多くの方法で計画を取得するために使用できます。

select * from table(dbms_xplan.display_cursor(sql_id => '7c02yjs9q5kqr'));

SQL_ID  7c02yjs9q5kqr, child number 0
-------------------------------------
SELECT VALUE(VD) FROM VW_DEPT VD WHERE EXISTS (SELECT 1 FROM TABLE(:B1 
) VV WHERE VD.DEPTNO = VV.DEPTNO)

Plan hash value: 2616009478

-------------------------------------------------------------------------------------------
| Id  | Operation                          | Name | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                   |      |       |       |    32 (100)|          |
|*  1 |  HASH JOIN SEMI                    |      |     1 |    32 |    32   (4)| 00:00:01 |
|   2 |   TABLE ACCESS FULL                | DEPT |     1 |    30 |     2   (0)| 00:00:01 |
|   3 |   COLLECTION ITERATOR PICKLER FETCH|      |  8168 | 16336 |    29   (0)| 00:00:01 |
-------------------------------------------------------------------------------------------

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

   1 - access("DEPTNO"=SYS_OP_ATG(VALUE(KOKBF$),8,9,2))

/*+ gather_plan_statistics */ヒント、v$sql_monitorv$sql_plan、などを使用して、同様の情報を取得する関連する方法が多数ありますdbms_sqltune.report_sql_monitor(sql_id => '7c02yjs9q5kqr', type => 'active')。それぞれに異なる長所と短所があります。個人的には、report_sql_monitor可能な限り使用するようにしています。追加のライセンスが必要ですが、これはクエリの優れたグラフィカル表現です。

4 - 変数の代わりに関数を使用します。

create or replace package test_pkg is
    v_emp typ_emp_coll;
    function get_v_emp return typ_emp_coll;
end;
/

create or replace package body test_pkg is
    function get_v_emp return typ_emp_coll is
    begin
        return v_emp;
    end;
end;
/

これで、クエリをスタンドアロンの SQL ステートメントとして実行し、実際のコレクション データを使用できるようになりました。通常の説明計画メソッドは正常に機能するはずです。

select * from table(test_pkg.get_v_emp);

なぜ計画はいつもとても悪いのですか?

どちらの方法を使用しても、おそらく私が生成したものとほぼ同じ説明プランが表示されます。これは、関数とコレクションがオプティマイザーから見えず、8168毎回行を推測するだけだからです。

これらのクエリを調整するには、パイプライン関数とテーブル関数のカーディナリティを設定するこの記事を参照することをお勧めします。

動的サンプリングを使用したソリューションの簡単な例を次に示します。

select /*+ dynamic_sampling(5) */ * from table(test_pkg.get_v_emp)
于 2012-04-30T03:30:18.663 に答える