Showing posts with label Query Transformation. Show all posts
Showing posts with label Query Transformation. Show all posts

Friday, June 5, 2009

Query Transformationと実行計画の予測可能性

「クェリだけを見ても実行計画を予測することができるだろうか」と言う質問を時々受けます。

返事はいつも「それは純真な考えだと思います」です。

とてもシンプルに見えるSQL文でさえ不可能ものです。なぜそうでしょうか。

Oracleが積極的にQuery Transformationを実行するからです。

簡単な例で説明して見ましょう。

このSQL文はどのように実行計画が作られましょうか。


UKJA@ukja102> explain plan for
2 select /*+ */
3 outer.*
4 from ( select * from scott.emp outer
5 union all
6 select * from scott.emp outer) outer
7 where outer.sal > (select /*+ */ avg(inner.sal)
8 from scott.emp inner
9 where inner.deptno = outer.deptno
10 );

Explained.


私のデータベースには次のような実行計画に表現されます。十分に予測可能な実行計画だと思っていませんか。


-----------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 87 | 9 (0)| 00:00:01 |
|* 1 | FILTER | | | | | |
| 2 | VIEW | | 28 | 2436 | 6 (0)| 00:00:01 |
| 3 | UNION-ALL | | | | | |
| 4 | TABLE ACCESS FULL| EMP | 14 | 518 | 3 (0)| 00:00:01 |
| 5 | TABLE ACCESS FULL| EMP | 14 | 518 | 3 (0)| 00:00:01 |
| 6 | SORT AGGREGATE | | 1 | 7 | | |
|* 7 | TABLE ACCESS FULL | EMP | 5 | 35 | 3 (0)| 00:00:01 |
-----------------------------------------------------------------------------

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

1 - filter("OUTER"."SAL"> (SELECT AVG("INNER"."SAL") FROM
"SCOTT"."EMP" "INNER" WHERE "INNER"."DEPTNO"=:B1))
7 - filter("INNER"."DEPTNO"=:B1)



でも、subqueryにunnestヒントを付けるものだけで実行計画に大きい変化が発生します。Subquery UnnestingによりSubqueryがInline Viewに変わってしまいます。


UKJA@ukja102> explain plan for
2 select /*+ */
3 outer.*
4 from ( select * from scott.emp outer
5 union all
6 select * from scott.emp outer) outer
7 where outer.sal > (select /*+ unnest */ avg(inner.sal)
8 from scott.emp inner
9 where inner.deptno = outer.deptno
10 );

Explained.

--------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 113 | 11 (19)| 00:00:01 |
|* 1 | HASH JOIN | | 1 | 113 | 11 (19)| 00:00:01 |
| 2 | VIEW | VW_SQ_1 | 3 | 78 | 4 (25)| 00:00:01 |
| 3 | HASH GROUP BY | | 3 | 21 | 4 (25)| 00:00:01 |
| 4 | TABLE ACCESS FULL| EMP | 14 | 98 | 3 (0)| 00:00:01 |
| 5 | VIEW | | 28 | 2436 | 6 (0)| 00:00:01 |
| 6 | UNION-ALL | | | | | |
| 7 | TABLE ACCESS FULL| EMP | 14 | 518 | 3 (0)| 00:00:01 |
| 8 | TABLE ACCESS FULL| EMP | 14 | 518 | 3 (0)| 00:00:01 |
--------------------------------------------------------------------------------


PUSH_PREDヒントを付けるともっと極的な変化が現れます。Hash JoinがNested Loops Joinに変わり、Join条件がViewの内に入られます。これがUNION ALL PUSHED PREDICATEで表現されます。


UKJA@ukja102> select /*+ gather_plan_statistics PUSH_PRED(OUTER) */
2 outer.*
3 from ( select * from scott.emp outer
4 union all
5 select * from scott.emp outer) OUTER
6 where outer.sal > (select /*+ QB_NAME(SUB) UNNEST */ avg(inner.sal)
7 from scott.emp inner
8 where inner.deptno = outer.deptno
9 );


----------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows |
----------------------------------------------------------------------------
| 1 | NESTED LOOPS | | 1 | 1 | 12 |
| 2 | VIEW | VW_SQ_1 | 1 | 3 | 3 |
| 3 | HASH GROUP BY | | 1 | 3 | 3 |
| 4 | TABLE ACCESS FULL | EMP | 1 | 14 | 14 |
| 5 | VIEW | | 3 | 1 | 12 |
| 6 | UNION ALL PUSHED PREDICATE | | 3 | | 12 |
|* 7 | TABLE ACCESS FULL | EMP | 3 | 1 | 6 |
|* 8 | TABLE ACCESS FULL | EMP | 3 | 1 | 6 |
----------------------------------------------------------------------------

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

7 - filter(("OUTER"."SAL">"VW_COL_1" AND "OUTER"."DEPTNO"="DEPTNO"))
8 - filter(("OUTER"."SAL">"VW_COL_1" AND "OUTER"."DEPTNO"="DEPTNO"))


CBQT(Cost Based Query Transformation)を非活性化するとヒントがなくても同じ効果を得られます。


UKJA@ukja102> alter session set "_optimizer_cost_based_transformation" = off;

UKJA@ukja102> alter session set "_optimizer_push_pred_cost_based" = false;

UKJA@ukja102> explain plan for
2 select /*+ */
3 outer.*
4 from ( select * from scott.emp outer
5 union all
6 select * from scott.emp outer) outer
7 where outer.sal > (select /*+ */ avg(inner.sal)
8 from scott.emp inner
9 where inner.deptno = outer.deptno
10 );

Explained.

----------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)|
----------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 65 | 9 (23)|
|* 1 | HASH JOIN | | 1 | 65 | 9 (23)|
| 2 | VIEW | VW_SQ_1 | 3 | 78 | 4 (25)|
| 3 | HASH GROUP BY | | 3 | 21 | 4 (25)|
| 4 | TABLE ACCESS FULL | EMP | 14 | 98 | 3 (0)|
| 5 | VIEW | | 28 | 1092 | 4 (0)|
| 6 | UNION-ALL PARTITION| | | | |
| 7 | TABLE ACCESS FULL | EMP | 14 | 518 | 3 (0)|
| 8 | TABLE ACCESS FULL | EMP | 14 | 518 | 3 (0)|
----------------------------------------------------------------------


Oracle 10gからはQuery Transformationの大部分をOracle自身で決定します。したがって、SQL文を見るだけで実行計画を予測するのはほとんど不可能です。

重要なものはいつも実際の実行計画をきちんと確認し、SQL文がどんな順序で実行されるかを理解するのです。無理な予測は役に立たないだけでなく、むしろ危ないことになってしまうかも知れません。