Oracle:Viewにヒント句を指定する方法 | けしくんのWebLog

けしくんのWebLog

自分が考えたこと、調べたことを忘れずに残しておくため、Web上にLogを残していきます。

SQLチューニングを行っていると、Viewに対してヒントを指定したくなることがある。

ある用途のためにViewの定義にヒント句を書いてしまうと、

別の用途でViewを使用した時に良くない実行結果になってしまうことがあるため推奨されない。

ViewへのヒントはTableのときと同じようには記述できないことに注意する。

 

よく使うOracle SQL/コマンドまとめ

 

 

方法1:グローバルヒントを使う

これは単一表に対するヒントを指定したい場合(INDEXなど1つの表のみ指定するもの)、

最も簡単な方法となる。

複数表ヒント(LEADINGなど2つ以上の表を指定するもの)には使用できない等の制約があるため、

万能ではないことに注意する。

 

(例)View(V1)があり、View内のTable(T1、T2がある)T1に対して、Index(I1)を指定するヒント

SELECT /*+  INDEX( V1.T1 I1)  */
FROM V1

 

※View内のTableを指定する構文 ⇒ VIEW名.TABLE名

View内のTableに別名が指定されている場合 ⇒ VIEW名.TABLE別名.TABLE名

 

方法2:問合せブロック(Query Block)付きヒントを使う

これは単一表ヒント、複数表ヒントの両方に使用できる。

問合せブロックとは、View(副問合せ(インラインビュー)もViewと考える)で定義されたSELECT文のことである。

 

問合せブロックの名前は、以下の2通りで指定する。

・システムが自動的にブロック毎につけたもの

・自分で定義するもの(QB_NAMEヒントを入れる)

通常、Viewに対して自分で定義する問合せブロック名というのは指定できない(Viewの定義の方に書かれているSELECT文にQB_NAMEヒントを入れれば出来るかもしれないが未確認)ため、

実際にSQLを実行(あるいはEXPLAIN PLANを実行)して実行計画を確認する際に、

問合せブロック名も含めて確認するという作業が必要になる。

 

実行計画を確認する際に、問合せブロック名まで表示させるSQL

1.実際に実行したSQL IDがわかっている場合

 (Oracle:実行されているSQLをリアルタイムで監視し、問題のあるSQLの実行計画を確認する

  にあるツールやSQLを使って確認できる)

set linesize 300
set pagesize 50000
set trimspool on
SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR('SQL ID名',0,'ALL ALLSTATS LAST'));

※ パラメータは、ALLでなくてALIASでもよい

 

2.EXPLAIN PLANを実行した直後に確認する場合

set linesize 300
set pagesize 50000
set trimspool on
SELECT PLAN_TABLE_OUTPUT FROM TABLE(DBMS_XPLAN.DISPLAY(format=>'ALL ALLSTATS LAST'));

※ formatのパラメータのうち、ALLの部分はALIASでもよい

 

<実行例>

以下のSQLを実行した際の実行計画確認

explain plan for select * from v1;
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------
Plan hash value: 3271411982

------------------------------------------------------
| Id  | Operation                    | Name | E-Rows |
------------------------------------------------------
|   0 | SELECT STATEMENT             |      |      1 |
|   1 |  NESTED LOOPS                |      |      1 |
|   2 |   NESTED LOOPS               |      |      1 |
|   3 |    INDEX FULL SCAN           | I1   |      1 |

|*  4 |    INDEX RANGE SCAN          | I2   |      1 |
|   5 |   TABLE ACCESS BY INDEX ROWID| T2   |      1 |
------------------------------------------------------

Query Block Name /
Object Alias (identified by operation id):
-------------------------------------------------------------

   1 - SEL$F5BB74E1
   3 - SEL$F5BB74E1 / T1@SEL$2
  
4 - SEL$F5BB74E1 / T2@SEL$2
   5 - SEL$F5BB74E1 / T2@SEL$2

※上記の例では、問合せブロック名は「SEL$F5BB74E1」であるが、ヒントとして指定する場合は、黄色文字の部分(Object Aliasの方)を使用する。

この場合、例えば、実行計画内のId=4のでINDEXを使用しているが、このINDEXは、「SEL$2」という問合せブロック内にあるT2のものというように読み取る。

 

T2テーブルでINDEXを使用しないようにするためNO_INDEXヒントをつけてみる。

ここで先ほど確認した問合せブロック名を使用する。

select /*+ NO_INDEX(@SEL$2 T2) */ * from v1;
PLAN_TABLE_OUTPUT
----------------------------------------------------------------------------
Plan hash value: 2669822726

----------------------------------------------------------------------------
| Id  | Operation          | Name | E-Rows |E-Bytes| Cost (%CPU)| E-Time   |
----------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |      |      1 |    21 |     2   (0)| 00:00:01 |
|   1 |  NESTED LOOPS      |      |      1 |    21 |     2   (0)| 00:00:01 |

|   2 |   TABLE ACCESS FULL| T2   |      1 |    14 |     2   (0)| 00:00:01 |
|*  3 |   INDEX RANGE SCAN | I1   |      1 |     7 |     0   (0)| 00:00:01 |
----------------------------------------------------------------------------

Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------

   1 - SEL$F5BB74E1
   2 - SEL$F5BB74E1 / T2@SEL$2
   3 - SEL$F5BB74E1 / T1@SEL$2

※T2へのアクセスが「TABLE ACCESS FULL」となった。

 

 

よく使うOracle SQL/コマンドまとめ