ねら~ITエンジニア雑記

やきうのお兄ちゃんが綴るOracle Databaseメインのブログ

EXPLAIN PLAN FOR 〜(※AUTOTRACE TRACEONLY含む)で出てくる実行計画 と SQL実行時の実行計画が異なるケースを作ってみる。

EXPLAIN PLAN FOR 〜(※AUTOTRACE TRACEONLY含む)で出てくる実行計画 と
SQL実行時の実行計画が異なるケースを作ってみるZe!(`・ω・)Ъ

幾つかパターンは有るんですが、WHERE句の範囲検索を
バインド変数で指定するケースでやってみます。簡単なんで。

まず服は脱がずに、データ作る。

DROP TABLE TBL_A PURGE;

CREATE TABLE TBL_A (
    C1 NUMBER
  , C2 DATE
);

ALTER TABLE TBL_A ADD CONSTRAINT TBL_A_PK PRIMARY KEY(C1) USING INDEX;

DECLARE
  TYPE t_array_A IS TABLE OF TBL_A%ROWTYPE INDEX BY BINARY_INTEGER;
  array_A t_array_A;
BEGIN
  array_A.DELETE;
  FOR i IN 1..10000
  LOOP
    array_A(i).C1 := i;
    array_A(i).C2 := TO_DATE('20150723', 'YYYYMMDD') + (i/24/60/60);
  END LOOP;
  FORALL i in array_A.FIRST..array_A.LAST
    INSERT INTO TBL_A VALUES array_A(i);
  COMMIT;
END;
/

EXEC DBMS_STATS.GATHER_TABLE_STATS(USER, 'TBL_A', NO_INVALIDATE => FALSE);

SQL> SELECT COUNT(*) FROM TBL_A;

  COUNT(*)
----------
     10000

SQL>

次に不等号入りのSQLをEXPLAIN PLANしてみます。INDEX RANGE SCAN のプランやで!

EXPLAIN PLAN FOR
SELECT * FROM TBL_A WHERE C1 <= :B1;

Explained.

SET LINESIZE 170;
SET PAGESIZE 1000;
SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY('PLAN_TABLE', NULL, 'ALL'));

PLAN_TABLE_OUTPUT
-----------------------------------------------------------
Plan hash value: 3263267004

----------------------------------------------------------------------------------------
| Id  | Operation                   | Name     | Rows  | Bytes | Cost (%CPU)| Time     |
----------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |          |   500 |  6000 |     3   (0)| 00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID| TBL_A    |   500 |  6000 |     3   (0)| 00:00:01 |
|*  2 |   INDEX RANGE SCAN          | TBL_A_PK |    90 |       |     2   (0)| 00:00:01 |
----------------------------------------------------------------------------------------26 rows selected.

バインド変数使用/SET AUTOTRACE TRACEONLY で実際にSQLを実行してみる。
10,000件全てを取得する SQL なんで、TABLE を FULL SCAN する実行計画が期待値ですが、、、

VAR B1 NUMBER;
EXEC :B1 := 10000;
SET AUTOTRACE TRACEONLY;
-- SQL_ID = 'gqr0w2pjvq3mv'
SELECT * FROM TBL_A WHERE C1 <= :B1;

10000 rows selected.

Execution Plan
----------------------------------------------------------
Plan hash value: 3263267004

----------------------------------------------------------------------------------------
| Id  | Operation                   | Name     | Rows  | Bytes | Cost (%CPU)| Time     |
----------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |          |   500 |  6000 |     3   (0)| 00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID| TBL_A    |   500 |  6000 |     3   (0)| 00:00:01 |
|*  2 |   INDEX RANGE SCAN          | TBL_A_PK |    90 |       |     2   (0)| 00:00:01 |
----------------------------------------------------------------------------------------
:
Statistics
----------------------------------------------------------696  consistent gets
:
      10000  rows processed

INDEX RANGE SCAN のプラン……?と思いきやなんすが、AUTOTRACE で出て来るプランは
内部的に EXPLAIN PLAN 〜 をしているので、実際の実行計画とは異なる場合があります。

DBMS_XPLAN.DISPLAY_CURSOR で共有プールに格納された実際の実行計画を抜いてみます。

SET AUTOTRACE OFF;
SET LINESIZE 170;
SET PAGESIZE 1000;
SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR('gqr0w2pjvq3mv', NULL, 'ALL'));

PLAN_TABLE_OUTPUT
-----------------------------------------------------------
SQL_ID  gqr0w2pjvq3mv, child number 0
-------------------------------------
SELECT * FROM TBL_A WHERE C1 <= :B1

Plan hash value: 723866848

---------------------------------------------------------------------------
| Id  | Operation         | Name  | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |       |       |       |     9 (100)|          |
|*  1 |  TABLE ACCESS FULL| TBL_A |  8001 | 96012 |     9   (0)| 00:00:01 |
---------------------------------------------------------------------------

彡(^)(^)「TABLE FULL SCAN の実行計画だったやで!」

このように EXPLAIN PLAN FOR〜(※AUTOTRACE TRACEONLY含む)で出て来る実行計画と、
実際の実行計画は異なるケースが有るから、気を付けるんやで。下記マニュアルも参照!

Oracle® Databaseパフォーマンス・チューニング・ガイド
11gリリース2 (11.2)
B56312-06
12 EXPLAIN PLANの使用方法
12.1.4 EXPLAIN PLANの制限事項
http://docs.oracle.com/cd/E16338_01/server.112/b56312/ex_plan.htm
 > 一般にバインド変数では、EXPLAIN PLANが実際の実行計画を表していない場合があります。

EXPLAIN PLAN FOR〜はSQLを実行せずに実行計画を確認できるので非常に
便利なんですが、この記事で紹介したケース、云わば落とし穴が有ります。

DBMS_XPLAN.DISPLAY_CURSOR も併用して、共有プールに格納された
実際の実行計画も見るようにしませう(´・ω・)ゞ

関連記事:
SQLの実行計画を出力:id:gonsuke777:20110826:1314340326】
【共有プール上の実行計画を出力する。(DBMS_XPLAN.DISPLAY_CURSOR):id:gonsuke777:20120612:1339488014】