記事一覧へ戻る

 

掲載元
Oracle Magazine
January/February 2013

テクノロジー: Ask Tom

  

過去の記事、制限、データ・ロードについて

著者:Tom KyteOracle Employee ACE

 

オラクルの技術者が、これまでに述べられていなかった情報、カスタムのサイズ、外部表のヒントについて説明します。

Steven Feuersteinが、これまでOracle Magazineで発行された全記事を含むURLを投稿しています。 oracle.com/technetwork/issue-archive/index-087690.html

同様のリンクがあれば、ここに投稿していただけますか。

はい。 Oracle Magazineでは、Ask Tomなどの連載コラムについて、過去の記事のアーカイブと、すべての記事へのリンクを記載したページを管理しています。 私の記事へのリンクを記載したページはoracle.com/technetwork/issue-archive/index-093676.htmlにあります。 また、Oracle Magazineホームページ(oracle.com/technetwork/oramag/magazine/home)にも、過去の全記事に対するリンクや、これらのリスト・ページに対するリンクがあります(ホームページの下側、FEATURED COLUMNSの下にあります)。

UNDOサイズの制限

UNDO表領域のサイズを制限すべきだと考えている同僚がいます。このケースでは、2~5ノードのOracle Real Application Clustersシステムで、ノードあたり1GBまでに制限しています。 UNDOのサイズを抑えることで大きなサイズのトランザクションを制限するというのがその理由です (また、同僚はTEMP表領域にも同様の制限を加えています)。 私はこの手法は好きではありませんが、 ご意見をお聞かせください。

リソースに制限を加える方法としては、おかしな方法であり、むしろ独断的にも思えます。 “トランザクションのサイズを制限する”という要件を満たす意味では、この方法は私の解決策の候補にもなりません。 この方法をとっても、個々のトランザクションのサイズは制限されません。同時に実行されるすべてのトランザクションの累積サイズが1GBに制限されるだけであり、 サイズが1GBの単一のトランザクションを実行することは可能です。

トランザクションのサイズを抑えるための正しい方法は、Oracle DatabaseのResource Manager機能を使用することです。 すでに、ほぼすべてのシステムでResource Managerが使用されているはずです。お使いのシステムでまだResource Managerを使用していない場合は、ぜひ を読んで調査してください。 Resource Managerを使用すれば、特に、UNDO領域のUNDO割当て制限を定めることができます。 UNDO割当て制限により、リソース・コンシューマ・グループが任意の時点で利用できるUNDO量が制限されます。 この方法で、アプリケーション、ユーザー・グループ、または個々のユーザーが生成できるUNDO量を制限できます。

ここで再度、質問にある1GBという独断的な制限について考えます。 実際は、1GBのUNDOが必要なシステムもあれば、500MBを必要とするシステム、10GBを必要とするシステムもあり、システムによって必要となるサイズは異なります。 必要となるUNDO量の基準となるものは、以下のとおりです。

  • 同時トランザクション数

  • これらのトランザクションの相対サイズ

  • 実行時間が最長となる問合せの長さ(UNDO保存期間が不十分なことにより発生するORA-1555、“Snapshot too old”エラーを防ぐため)

Oracle9i Databaseで自動UNDO管理が導入される以前は、DBAはUNDOのサイズを手動で設定する必要がありました。 今、私が自分でUNDOのサイズを設定するとすれば、以下の手順を実行します。

  1. 必要なUNDO保存期間を決定します。 これは、実行時間が最長となる問合せを考慮して算出します(UNDO保存期間はこの問合せの実行時間よりも長くする必要があります。そうしなければ恐ろしいORA-1555エラーが発生します)。さらに、予想外のエラーが発生してもバックアップを使用せずにリカバリできるように、フラッシュバック表またはフラッシュバック問合せ操作を実行可能な状態にしておきたいため、この期間も考慮します。 UNDO保存期間は、通常のケースでは少なくとも3時間はとります。また、予想外のエラーに対しては、バックアップを使用するよりもフラッシュバック操作を実行する方が簡単であるため、さらに可能な限り長い期間を確保します。 Oracle9i Database以降、私はフラッシュバック機能によって数え切れないほど何度も救われました。

  2. UNDOセグメントの自動拡張をオンにして、必要なUNDO保存期間を満たす適度な制限を設定します。 無限に設定する必要はなく、何らかの適度なサイズに設定します。

UNDO表領域の自動拡張をオンにした場合、データベースではUNDO_RETENTION設定に指定した期間以上、UNDOが保持されます。長時間実行される問合せを完了できるように、UNDOがもっと長い期間保管されることもあります。

一時表領域も上記と同様のルールに従います。 一部のシステムでは、一時領域は使用されない可能性があります。 たとえば、オンライン・トランザクション処理(OLTP)システムでは同時に多数の行を操作するため、一時領域は実質的に必要ありません。 レポート作成システムやデータウェアハウスなどの他のシステムでは、問合せでの必要領域や同時に実行される問合せの数に応じて、数ギガバイト、あるいは数テラバイトもの一時領域が必要となる可能性があります。 UNDOに対しては、1つのサイズだけですべての状況に対応するという考え方はうまくいきません。

難しいデータ・ロード

SQL Loaderを使用して、データをフラット・ファイルから表にロードしようとしています。 ファイルのサンプル・データは次のとおりです。

 12,smith,1234556@@1234567 @@876556612,1200  14,John.1234@@4567@@56789  @@12345@@45778@@34566@@23456,1345 

フィールド値はそれぞれ、社員番号、社員名、電話番号、給料を表します。 この表の構造は次のとおりです。

 create table emp ( empno number(5) primary key, ename varchar2(10), Sal number(10,2) ) create table emp_contact ( empno references emp, phone_no number(10) )  

このデータをEMP表とEMP_CONTACT表に挿入したいのですが、 PHONE_NOの値がファイルにいくつ含まれているのかは分かりません(PHONE_NOの値の数は可変です)。 このデータをどのように表に挿入すれば良いですか。

これは、1行のデータをEMP_CONTACT表内で複数の行に変換しなければならないため難しい操作です。

SQL Loader(20世紀のレガシーなデータ・ロード・ツール)を使用する場合、この操作を直接行うのは不可能です。 DUALに対してビューを作成し、そのビューに対してINSTEAD OF INSERTトリガーを配置して、データを手続き的に処理することは可能です。 この場合、行が挿入されるたびに、トリガーで1行ずつ解析した後、それらの行(単数行または複数行)をまとめてEMPとEMP_CONTACTに挿入します。 ただし、この方法はお勧めしません。コード量が非常に多くなり、スケーラビリティがあまり良くないからです。 このトリガーを使用する方法では、1行ずつ処理されて遅くなるため、データ・ロードの良好なパフォーマンスが得られません。

代わりに、21世紀のデータ・ロード・ツールである、外部表を使用することをお勧めします。 この方法をテストするために、まずはリスト1に示す外部表を作成します。この外部表を使用することで、通常のデータベース表にあるデータと同様に扱って、データを問い合わせることができます。

コード・リスト1:外部表の作成

SQL> create or replace directory my_dir as '/home/tkyte'
  2  /
Directory created.

SQL> CREATE TABLE et
  2  ( "EMPNO" VARCHAR2(10),
  3    "ENAME" VARCHAR2(20),
  4    "TELNOS" VARCHAR2(1000),
  5    "SAL" VARCHAR2(10) )
  6  ORGANIZATION external
  7  (
  8    TYPE oracle_loader
  9    DEFAULT DIRECTORY MY_DIR
 10    ACCESS PARAMETERS
 11    (
 12      RECORDS DELIMITED BY NEWLINE CHARACTERSET US7ASCII
 13      BADFILE 'MY_DIR':'t.bad'
 14      LOGFILE 't.log_xt'
 15      READSIZE 1048576
 16      FIELDS TERMINATED BY "," OPTIONALLY ENCLOSED BY '"' LDRTRIM
 17      MISSING FIELD VALUES ARE NULL
 18      REJECT ROWS WITH ALL NULL FIELDS
 19      (
 20        "EMPNO" CHAR(255)
 21          TERMINATED BY "," OPTIONALLY ENCLOSED BY '"',
 22        "ENAME" CHAR(255)
 23          TERMINATED BY "," OPTIONALLY ENCLOSED BY '"',
 24        "TELNOS" CHAR(1000)
 25          TERMINATED BY "," OPTIONALLY ENCLOSED BY '"',
 26        "SAL" CHAR(255)
 27          TERMINATED BY "," OPTIONALLY ENCLOSED BY '"'
 28      )
 29    )
 30    location
 31    (
 32      't.dat'
 33    )
 34  )
 35  /
Table created.

SQL> select * from et;

EMPNO  ENAME   TELNOS                                          SAL
—————  —————   —————————————————————————————————————————————   ————
12     smith   1234556@@1234567@@876556612                     1200
14     John    1234@@4567@@56789@@12345@@45778@@34566@@23456   1345
 

これで、SQLを使用してデータをロードできるようになりました。そのため、これ以降はSQLを使用する、あらゆる操作が可能になります。 必要な操作は、リスト1内で取得した行ごとに、TELNOS文字列の‘@@’文字列周辺を解析し、各電話番号に対応する行を出力することです。 SQLを使用すれば、この操作をリスト2のように簡単に実行できます。

コード・リスト2:各電話番号に対応する行を出力するための外部表の問合せ

SQL> select empno, ename, sal, i,
  2         substr( tnos,
  3                 instr( tnos, '@@', 1, i )+2,
  4                 instr( tnos, '@@', 1, i+1 )
  5                   -instr( tnos, '@@', 1, i) - 2 ) tno
  6    from (
  7  select to_number(et.empno) empno,
  8         et.ename,
  9         to_number(et.sal) sal,
 10         column_value i,
 11         '@@'||et.telnos||'@@' tnos
 12    from et,
 13         table( cast( multiset(
 14            select level
 15              from dual
 16           connect by level <=
 17              (length(et.telnos)
 18                 -length(replace(et.telnos,'@@','')))/2+1 )
 19               as sys.odciNumberList ) )
 20         )
 21  /

EMPNO  ENAME    SAL    I  TNO
—————  —————   ————   ——  ———————
   12  smith   1200    1  1234556
   12  smith   1200    2  1234567
   12  smith   1200    3  876556612
   14  John    1345    1  1234
   14  John    1345    2  4567
   14  John    1345    3  56789
   14  John    1345    4  12345
   14  John    1345    5  45778
   14  John    1345    6  34566
   14  John    1345    7  23456

10 rows selected.

データ・ロードの説明に進む前に、リスト2の問合せについて説明します。 外部表にあるそれぞれの行を複数の行に変換する必要があり、その行数はET.TELNOS列にある‘@@’文字列の数に1を加えたものです。そのため、17行目と18行目で、この行数を計算しています。 さらに、CONNECT BYを使用して、その複数の行を生成しています。

13~19行目では、各入力行に対して、必要となる行セットを生成しています。 1つ目の入力レコードに対して3行、2つ目の入力レコードに対しては7行生成されます。 次に、このデータセットを外部表自体に、1行ずつ結合しています。 以下のような構成メンバーを使用している場合、結合条件は必要ありません。

SELECT * 
FROM some_table, TABLE( some_set )
 

TABLE()句は、SOME_TABLE内のそれぞれの行に対して実行され、これらの行が、その行の生成元の行と結合されます。 そのため、12~20行目は、ET外部表の1行目を3回返し、2行目を7回返します。これが、まさに必要な操作です。 7~11行目では、この出力から必要となる列を選択して整形しています。 EMPNO列とSAL列を適切なデータ型に変換し、ENAMEフィールドはそのまま残し、TELNOS文字列の前後に‘@@’を追加して一時的に解析しやすくしています。 また、この表の関数の出力を取得し(列には暗黙的にCOLUMN_VALUEという名前が付けられています)、列にIという名前を付けます。 I列は、入力レコードごとの行番号を表します。出力結果から分かるように、1つ目のレコードに対して1~3、2つ目のレコードに対して1~7の番号が付けられています。

最後に、1~5行目で、必要なフィールドを選択して、TNOS文字列のn番目の電話番号を解析しています。 3行目でINSTRを使用して、その文字列内のn番目の‘@@’を見つけています。4行目で再度INSTRを使用して、(n+1)番目の‘@@’を調べています。 これで、文字列内のn番目の電話番号エントリの開始文字と長さを計算して、今求められている出力を取得できます。

次に、データをロードするために、リスト2の各グループの1行目(I = 1の行)をEMP表に挿入し、さらに各グループの全行をEMP_CONTACT表に挿入する必要があります。 これは、データを2周分操作することで実行できます。つまり、単純にET表をEMPにロードし、次にリスト2の問合せの出力をEMP_CONTACTにロードします。 ただしここでは、データを2周分処理するのではなく、複数表の挿入の機能を使用して、両方の表を1文でロードすることにします。この文は、リスト3のようになります。

コード・リスト3:EMP表とEMP_CONTACT表の両方に行をロードするための複数のINSERT

SQL> create table emp
  2  ( empno number primary key,
  3    ename varchar2(10),
  4    sal   number
  5  );
Table created.

SQL> create table emp_contact
  2  ( empno    number references emp,
  3    phone_no number
  4  );
Table created.
\
SQL> insert all
  2  when (i = 1) then into emp (empno,ename,sal) values (empno,ename,sal)
  3  when (i > 0) then into emp_contact(empno,phone_no) values (empno,tno)
  4  select empno, ename, sal, i,
  5         substr( tnos,
  6                 instr( tnos, '@@', 1, i )+2,
  7                 instr( tnos, '@@', 1, i+1 )
  8                   -instr( tnos, '@@', 1, i) - 2 ) tno
  9    from (
 10  select to_number(et.empno) empno,
 11         et.ename,
 12         to_number(et.sal) sal,
 13         column_value i,
 14         '@@'||et.telnos||'@@' tnos
 15    from et,
 16         table( cast( multiset(
 17            select level
 18              from dual
 19           connect by level <=
 20              (length(et.telnos)
 21                 -length(replace(et.telnos,'@@','')))/2+1 )
 22               as sys.odciNumberList ) )
 23         )
 24  /
12 rows created.

SQL> select * from emp;

EMPNO  ENAME    SAL
—————  ——————  —————
   12  smith   1200
   14  John    1345

SQL> select * from emp_contact;

EMPNO    PHONE_NO
———————  —————————
   12     1234556
   12     1234567
   12   876556612
   14        1234
   14        4567
   14       56789
   14       12345
   14       45778
   14       34566
   14       23456

10 rows selected.
 

ここでは、ET外部表に対し、1つのSQL文を使用して、EMP表とEMP_CONTACT表を同時にロードしています。 このように、外部表をデータ・ロード・ツールとして使用することで、SQL Loaderでは不可能だった新たな可能性が広がります。

カーディナリティ

コレクションを使用した結合とカーディナリティの見積りについて質問があります。 あるコレクションについて、オプティマイザからは常に8,168行と返され、効率の悪い計画を使用しているという見積りが示されます。 しかし、8,168という見積りは、実際のカーディナリティよりも2桁多い数値です。 この問題を解決する方法はありますか。

これは、最適化中のパイプライン関数とコレクションに付随する、昔から存在する問題です。 オプティマイザは一般的に、コレクションにより返されるカーディナリティ(行数)に関する情報を保持していません。 オプティマイザはカーディナリティを推測する必要があります。そして、その推測はブロック・サイズに基づいています(デフォルトの統計はブロック・サイズによって決定されます)。 そのため、ブロック・サイズ8Kのデータベースの場合、推測値は約8,000となります。 コレクションに約8,000要素が含まれる状況はあまりないため(ほとんどのケースで、8、あるいは80などの方が当てはまるでしょう)、最終的な計画は当然ながら次善のものになります。

そのため、この質問は、オプティマイザにカーディナリティの適切な見積り値を伝えるにはどうすれば良いか、ということになります。 以下、4つの方法について説明し、さらに拡張可能なオプティマイザを使用するという5つ目の方法について、参照情報を示します。 4つの方法は以下のとおりです。

  • カーディナリティ・ヒント(文書なし)

  • OPT_ESTIMATEヒント(文書なし)

  • 動的サンプリング(Oracle Database 11g Release 1以降)

  • Oracle Databaseのカーディナリティ・フィードバック機能(Oracle Database 11g Release 2以降)

5つ目の方法については、Oracle ACEのAdrian Billingtonの記事 を参照してください。

適切なカーディナリティの見積り値を取得し、操作する方法を示すために、私がこの数年の間に何度も使用してきた、ある小さなパイプライン関数を使用します。 この関数は、リスト4に示すとおり、デリミタ付き文字列をセットに変換するものです。

コード・リスト4:デリミタ付き文字列をセットに変換するパイプライン関数

SQL> create or replace type str2tblType as table of varchar2(30)
  2  /
Type created. 

SQL> create or replace
  2  function str2tbl( p_str in varchar2, p_delim in varchar2 default ',' )
  3  return str2tblType
  4  PIPELINED
  5  as
  6      l_str      long default p_str || p_delim;
  7      l_n        number;
  8  begin
  9      loop
 10          l_n := instr( l_str, p_delim );
 11          exit when (nvl(l_n,0) = 0);
 12          pipe row( ltrim(rtrim(substr(l_str,1,l_n-1))) );
 13          l_str := substr( l_str, l_n+1 );
 14      end loop;
 15  end;
 16  /
Function created.
 

この関数をインストール後、以下のような問合せを実行して試すことができます。

SQL> variable x varchar2(15)

SQL> exec :x := '1,2,3,a,b,c'

PL/SQL procedure successfully completed.

SQL> select * from table(str2tbl(:x));

COLUMN_VALUE
——————————————————————————————————————
1
2
3
a
b
c

6 rows selected.

このようなパイプライン関数には、さまざまな使い方が思い浮かびます。たとえば、INリストのバインドに利用できるでしょう(bit.ly/S43Jqqにデモンストレーションがあります)。 ただし、この関数には、リスト5に示すように、オプティマイザによるカーディナリティの見積り値に関する問題があります。

コード・リスト5:オプティマイザによる8Kのカーディナリティの見積り

SQL> select * from table(dbms_xplan.display_cursor);

PLAN_TABLE_OUTPUT
————————————————————————————————————————————————————————
SQL_ID  ddk1tv9s5pzq5, child number 0
————————————————————————————————————————————————————————
select * from table(str2tbl(:x))

Plan hash value: 2407808827

———————————————————————————————————————————————————————————————————————————
|Id|Operation                      |Name   |Rows|Bytes|Cost (%CPU)|Time    |
———————————————————————————————————————————————————————————————————————————
| 0|SELECT STATEMENT               |       |    |     |  29  (100)|        |
| 1| COLLECTION ITERATOR PICKLER...|STR2TBL|8168|16336|  29    (0)|00:00:01|

ここでは、ROWS列に8,168という不自然な数値が出力されています。 オプティマイザは、この関数が返すコレクションに8,000を超えるエントリが今後挿入されると想定します。問合せ処理時の索引の使用の有無に関するオプティマイザの判断に対して、この想定がどう影響を及ぼすかはお分かりでしょう。 この問題に対する1つ目の解決策は、カーディナリティ・ヒントを使用することです(リスト6)。

コード・リスト6:カーディナリティ・ヒントの使用

SQL> select * from table(dbms_xplan.display_cursor);

PLAN_TABLE_OUTPUT
————————————————————————————————————————————————————————
SQL_ID  bd2f8rh30z3ww, child number 0
————————————————————————————————————————————————————————
select /*+ cardinality(sq 10) */ * from table(str2tbl(:x)) sq

Plan hash value: 2407808827

———————————————————————————————————————————————————————————————————————————
|Id|Operation                      |Name   |Rows|Bytes|Cost (%CPU)|Time    |
———————————————————————————————————————————————————————————————————————————
| 0|SELECT STATEMENT               |       |    |     |  29  (100)|        |
| 1| COLLECTION ITERATOR PICKLER...|STR2TBL|  10|   20|  29    (0)|00:00:01|

このように問合せ内でCARDINALITY(SQ 10)をヒントとして使用する場合、オプティマイザではカーディナリティの見積り値が、8,168ではなく、指定された数値へと調整されます。 ここで、この例の実際のカーディナリティは6ですが、6という数値を指定していない点に注意してください。 見積り値を正確に指定する必要はありません。データを十分に表現する数値を入力する必要はありますが、完全に一致しなくてかまいません。 通常は、実際の値の10倍以内に設定します(8,168は1,000倍もあります)。 このカーディナリティ・ヒントは、Oracle9i Database以降で動作します。

次の方法はSQLプロファイルと同様の方法であり、Oracle Database 10g以降で利用できます。 それは、OPT_ESTIMATEヒントを使用することです。 このケースでは、OPT_ESTIMATEヒントは3つの入力値を受け取ります。 入力値はそれぞれ、ヒントの適用先のタイプ(このケースでは表)、その表の相関名(このケースではSQ)、オプティマイザの見積り値に適用するスケール変更係数です。 このスケール変更係数は、求めるカーディナリティを算出するために、オプティマイザでのカーディナリティの見積り値に乗算する数のことです。 このケースでは、スケール変更係数は以下のようになります。

SQL> select 10/8168 from dual;

    10/8168
————————————————
  .00122429
 

ここでは8,168ではなく10という値を求めているため、0.00122429というスケール変更係数を使用する必要があります。 このスケール変更係数を使用すると、リスト7のような結果が得られます。

コード・リスト7:OPT_ESTIMATEヒントの使用

select /*+ opt_estimate(table, sq, scale_rows=0.00122429) */ * 
  from table(str2tbl(:x)) sq

Plan hash value: 2407808827

———————————————————————————————————————————————————————————————————————————
|Id|Operation                      |Name   |Rows|Bytes|Cost (%CPU)|Time    |
———————————————————————————————————————————————————————————————————————————
| 0|SELECT STATEMENT               |       |    |     |  29  (100)|        |
| 1|  OLLECTION ITERATOR PICKLER...|STR2TBL|  10|   20|  29    (0)|00:00:01|

これで、求められる結果(カーディナリティの見積り値10)を再度得ることができました。

3つ目に使用できる方法は、Oracle Database 11g Release 1以降の動的サンプリングです。 コレクションのサンプリングを行うために、問合せに動的サンプリング・ヒントを追加する必要があります。 セッション内、またはシステム・レベルで動的サンプリングのレベルを設定するだけでは不十分です。 リスト8に、動的サンプリング・ヒントを使用した問合せを示します。

コード・リスト8:動的サンプリング・ヒントの使用

select /*+ dynamic_sampling( sq, 2 ) */ * from table( str2tbl(:x,',') ) sq

Plan hash value: 2407808827

———————————————————————————————————————————————————————————————————————————
|Id|Operation                      |Name   |Rows|Bytes|Cost (%CPU)|Time    |
———————————————————————————————————————————————————————————————————————————
| 0|SELECT STATEMENT               |       |    |     |  11  (100)|        |
| 1| COLLECTION ITERATOR PICKLER...|STR2TBL|   6|   12|  11    (0)|00:00:01|
———————————————————————————————————————————————————————————————————————————

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

このケースでは、カーディナリティの見積り値は6です。なぜなら、この問合せを実行してサンプルを取得した結果、要素が6個あったからです。 この動的サンプリングの方法では、SQL文のハード解析により判明したカーディナリティが、それと同じカーソルを使用したその後のすべての実行に対して使用されます。

最後に、Oracle Database 11g Release 2以降に含まれるカーディナリティ・フィードバック機能について説明します。 この機能については、以前に簡単に紹介しました(bit.ly/Y7WSjM)。 この記事の執筆以降、カーディナリティ・フィードバックでコレクションとバインド変数を扱う場合の新しい要件が分かりましたので、その要件について以下で説明します。

カーディナリティ・フィードバックは、最初に問合せを実行した後に、実際のカーディナリティがカーディナリティの見積り値とかけ離れていることが検出された場合に、オプティマイザにカーディナリティの見積り値を変更させる機能です。 つまり、オプティマイザが失敗から学び始めます。 問合せを実行し、実際の行数が見積り数と大きく異なる場合に、オプティマイザでは新しく検出した値を使用して問合せを再度最適化します。

この機能をコレクションとバインド変数で利用できるようにするには、これまでの問合せを少し変更する必要があります。 副問合せのファクタリング(WITH)とマテリアライズ・ヒントを使用して、データベース内のコレクション・データを一時表内に物理的にマテリアライズします (このケースでは、カーディナリティ・フィードバックをバインド変数で利用するために、この作業が必要になります)。 リスト9に、副問合せのファクタリング(WITH)とマテリアライズ・ヒントを使用したカーディナリティ・フィードバックを示します。

コード・リスト9:カーディナリティ・フィードバックの使用(副問合せのファクタリング(WITH)とマテリアライズ・ヒントを含む)

with sq 
as (
select /*+ materialize */ *    
  from table( str2tbl( :x ) )
) 
select * 
  from sq

Plan hash value: 630596523

—————————————————————————————————————————————————————————————————————————————
|Id|Operation                        |Name   |Rows|Bytes|Cost (%CPU)|Time    |
—————————————————————————————————————————————————————————————————————————————
| 0|SELECT STATEMENT                 |       |    |     |  32  (100)|        |
| 1| TEMP TABLE TRANSFORMATION       |       |    |     |           |        |
| 2|  LOAD AS SELECT                 |       |    |     |           |        |
| 3|   COLLECTION ITERATOR PICKLER...|STR2TBL|8168|16336|   29   (0)|00:00:01|
| 4|  VIEW                           |       |8168| 135K|    3   (0)|00:00:01|
| 5|   TABLE ACCESS FULL             |SYS_...|8168|16336|    3   (0)|00:00:01|
—————————————————————————————————————————————————————————————————————————————

18 rows selected.
 

カーディナリティ・フィードバックを使用すると、これまでは実際の問合せを使用していた場所で、SQを使用できるようになります。 この問合せを最初に実行した際には、カーディナリティの見積り値は大きく外れ、以前と同様に8,168という不自然な数値になります。 しかしながら、オプティマイザはこの失敗から学びます。そのため、問合せを再度実行すると、リスト10の結果が得られます。

コード・リスト10:カーディナリティ・フィードバックによる修正後のカーディナリティの取得

with sq as (select /*+ materialize */ *    from table( str2tbl( :x ) )
) select * from sq

Plan hash value: 630596523

—————————————————————————————————————————————————————————————————————————————
|Id|Operation                        |Name   |Rows|Bytes|Cost (%CPU)|Time    |
—————————————————————————————————————————————————————————————————————————————
| 0|SELECT STATEMENT                         |    |     |  32  (100)|        |
| 1| TEMP TABLE TRANSFORMATION               |    |     |           |        |
| 2|  LOAD AS SELECT                         |    |     |           |        |
| 3|   COLLECTION ITERATOR PICKLER...|STR2TBL|8168|16336|  29    (0)|00:00:01|
| 4|  VIEW                                   |   6| 102 |   3    (0)|00:00:01|
| 5|   TABLE ACCESS FULL             |SYS_...|   6|   12|   3    (0)|00:00:01|
—————————————————————————————————————————————————————————————————————————————

Note
———————
   - cardinality feedback used for this statement

22 rows selected.
 

カーディナリティ・フィードバックによって、正しいカーディナリティの見積り値を取得できました。 一度失敗する必要がありますが、2回目以降はオプティマイザ自体が修正します。 以上のように、正しいコレクションのカーディナリティの見積り値を取得する方法は4つあります。その方法は、カーディナリティ・ヒント、OPT_ESTIMATEヒント、動的サンプリング、カーディナリティ・フィードバックです。

次のステップ


 ASK Tom
Tom Kyteが技術的な難しい疑問に回答しています。 このフォーラムのハイライトをこのコラムで紹介しています。

 TwitterでTomをフォロー
 

その他の記事、書籍
 Tomのその他の記事
 Oracle Database Concepts 11g Release 2 (11.2)
 Expert Oracle Database Architecture: Oracle Database 9i, 10g, and 11g Programming Techniques and Solutions、第2版

 ダウンロード Oracle Database 11g Release 2
 

詳細情報
 Oracle DatabaseのResource Manager機能
 拡張可能なオプティマイザ
 パイプライン関数
 Oracle Databaseのカーディナリティ・フィードバック機能

Oracle Databaseのフォロー
 Twitter
 Facebook
 


Tom Kyteの顔写真


Tom KyteはオラクルのServer Technologies部門に籍を置くデータベース・エバンジェリストで、1993年からオラクルに勤務しています。 Expert Oracle Database Architecture(Apress、2005年/2010年)、Effective Oracle by Design(Oracle Press、2003)などの著書があります。

 

 

 

▲ ページTOPに戻る

記事一覧へ戻る