天天看點

ORACLE資料庫SQL優化--->如何得到真實的執行計劃

在ORACLE資料庫裡通常可以使用如下的四種方法來得到目标SQL的執行計劃:

1,EXPLAIN PLAN指令

2,DBMS_XPLAN包

3,SQLPLUS中的AUTOTRACE開關

4,10046事件

除了第四種方法外,其他的三種方法得到的執行計劃都有可能不準确。在ORACLE資料庫中判斷得到的執行計劃是否準确,就是看目标SQL是否被真正的執行,真正執行過的SQL所對應的執行計劃就是準确的,反之,則有可能不準确。

對于使用第二種方法(DBMS_XPLAN)而言,針對不同的應用場景,你可以選擇如下四種方式的一種。

a, select * from table(dbms_xplan.display)

例如:

SQL> explain plan for select * from hr.employees;

Explained.

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

PLAN_TABLE_OUTPUT

--------------------------------------------------------------------------------

Plan hash value: 1445457117

-------------------------------------------------------------------------------

| Id  | Operation         | Name      | Rows  | Bytes | Cost (%CPU)| Time     |

-------------------------------------------------------------------------------

|   0 | SELECT STATEMENT  |           |   107 |  7276 |     3   (0)| 00:00:01 |

|   1 |  TABLE ACCESS FULL| EMPLOYEES |   107 |  7276 |     3   (0)| 00:00:01 |

-------------------------------------------------------------------------------

8 rows selected.

b,select * from table(dbms_xplan.display_cursor(null,null,'advanced'));

c,select * from table(dbms_xplan.display_cursor('sql_id/hash_value',child_cursor_number,'advanced'));

d,select * from table(dbms_xplan.display_awr('sql_id'));

Note:執行select * from table(dbms_xplan.display)所得到的執行計劃可能是不準确的,因為它隻是用于檢視使用explain plan指令得到的目标SQL的執行計劃,目标SQL此時還沒有真正執行,是以用它得到的執行計劃可能不正确。使用剩下的三種方式得到的執行計劃都是準确的,因為此時的目标SQL都已經被實際執行過了。

 對第三種方法(即使用SQLPLUS中的AUTOTRACE)而言,可以有下面幾種方法來開啟:

set autotrace on;(目标SQL都已經被執行)

set autotrace traceonly;(目标SQL都已經被執行)

set autotrace traceonly explain; (對于查詢目标SQL時,是沒有被實際執行,但是如果目标SQL是DML語句時,這個時候DML是實際上已經被執行了)

由于SET AUTOTRACE指令後顯示的執行計劃實際上是來源于調用EXPLIAN PLAN指令,而用EXPLAIN PLAN指令得到的執行計劃有可能不準确(特别是在使用了綁定變量的情況下),是以使用SET AUTOTRACE指令所顯示的執行計劃也有可能不準确。

看一個如下的例子來驗證下使用explain plan和set autotrace指令後得到的執行計劃并不是目标SQL真實執行計劃:

SQL> show user

USER is "HR"

SQL> create table T1 as select * from dba_objects;

Table created.

SQL> insert into t1 select * from t1;

50319 rows created.

SQL> commit;

Commit complete.

SQL> insert into t1 select * from t1;

100638 rows created.

SQL> commit;

Commit complete.

SQL> select count(*) from t1;

  COUNT(*)

----------

    201276

 在表T1的OBJECT_ID列上建立一個單鍵值的B樹索引IDX_T1

SQL> create index idx_t1 on t1(object_id);

Index created.

對T1表收集一下統計資訊:

SQL> exec dbms_stats.gather_table_stats(ownname=>'HR',tabname=>'T1',estimate_percent=>100,cascade=>true);

PL/SQL procedure successfully completed.

建立2個綁定變量X和Y,X=0,Y=100000

SQL> var x number;

SQL> var y number;

SQL> exec :x :=0

PL/SQL procedure successfully completed.

SQL> exec :y :=100000

PL/SQL procedure successfully completed.

檢視如下語句的執行計劃:

SQL> explain plan for select count(*) from t1 where object_id between :x and :y;

Explained.

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

PLAN_TABLE_OUTPUT

--------------------------------------------------------------------------------

Plan hash value: 2351893609

-----------------------------------------------------------------------------

| Id  | Operation          | Name   | Rows  | Bytes | Cost (%CPU)| Time     |

-----------------------------------------------------------------------------

|   0 | SELECT STATEMENT   |        |     1 |     5 |     3   (0)| 00:00:01 |

|   1 |  SORT AGGREGATE    |        |     1 |     5 |            |          |

|*  2 |   FILTER           |        |       |       |            |          |

|*  3 |    INDEX RANGE SCAN| IDX_T1 |   503 |  2515 |     3   (0)| 00:00:01 |

-----------------------------------------------------------------------------

PLAN_TABLE_OUTPUT

--------------------------------------------------------------------------------

Predicate Information (identified by operation id):

---------------------------------------------------

   2 - filter(TO_NUMBER(:X)<=TO_NUMBER(:Y))

   3 - access("OBJECT_ID">=TO_NUMBER(:X) AND "OBJECT_ID"<=TO_NUMBER(:Y))

16 rows selected.

從上面可以看出使用EXPLAIN PLAN得到的執行計劃顯示目标SQL走的是對索引IDX_T1的索引範圍掃描。

但是實際情況是否是這樣呢?我們實際執行下上面的語句:

SQL> select count(*) from t1 where object_id between :x and :y;

  COUNT(*)

----------

    201276

用DBMS_XPLAN.DISPLAY_CURSOR(NULL,NULL,'ADVANCED')得到目标SQL的真實執行計劃如下所示:

SQL> select * from table(dbms_xplan.display_cursor(null,null,'advanced'));

PLAN_TABLE_OUTPUT

--------------------------------------------------------------------------------

SQL_ID  9dhu3xk2zu531, child number 0

-------------------------------------

select count(*) from t1 where object_id between :x and :y

Plan hash value: 1410530761

--------------------------------------------------------------------------------

-

| Id  | Operation              | Name   | Rows  | Bytes | Cost (%CPU)| Time

|

PLAN_TABLE_OUTPUT

--------------------------------------------------------------------------------

--------------------------------------------------------------------------------

-

|   0 | SELECT STATEMENT       |        |       |       |   106 (100)|

|

|   1 |  SORT AGGREGATE        |        |     1 |     5 |            |

|

|*  2 |   FILTER               |        |       |       |            |

PLAN_TABLE_OUTPUT

--------------------------------------------------------------------------------

|

|*  3 |    INDEX FAST FULL SCAN| IDX_T1|   201K|   982K|   106   (7)| 00:00:02

|

--------------------------------------------------------------------------------

-

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

-------------------------------------------------------------

PLAN_TABLE_OUTPUT

--------------------------------------------------------------------------------

   1 - SEL$1

   3 - SEL$1 / [email protected]$1

Outline Data

-------------

Peeked Binds (identified by position):

--------------------------------------

   1 - :X (NUMBER): 0

   2 - :Y (NUMBER): 100000

PLAN_TABLE_OUTPUT

--------------------------------------------------------------------------------

Predicate Information (identified by operation id):

---------------------------------------------------

   2 - filter(:X<=:Y)

   3 - filter(("OBJECT_ID">=:X AND "OBJECT_ID"<=:Y))

Column Projection Information (identified by operation id):

-----------------------------------------------------------

   1 - (#keys=0) COUNT(*)[22]

PLAN_TABLE_OUTPUT

--------------------------------------------------------------------------------

51 rows selected.

從上面的執行計劃可以看出,現在目标SQL實際的執行計劃是走對索引IDX_T1的索引快速全掃描,這才是目标SQL的真實的執行計劃,即剛才用EXLPAIN PLAN指令得到的計劃是不準确的。

我們再來看下,使用SET AUTOTRACE 指令的情況。打開目前SESSION的AUTOTRACE:

SQL> set autotrace traceonly

SQL> select count(*) from t1 where object_id between :x and :y;

Execution Plan

----------------------------------------------------------

Plan hash value: 2351893609

-----------------------------------------------------------------------------

| Id  | Operation          | Name   | Rows  | Bytes | Cost (%CPU)| Time     |

-----------------------------------------------------------------------------

|   0 | SELECT STATEMENT   |        |     1 |     5 |     3   (0)| 00:00:01 |

|   1 |  SORT AGGREGATE    |        |     1 |     5 |            |          |

|*  2 |   FILTER           |        |       |       |            |          |

|*  3 |    INDEX RANGE SCAN| IDX_T1 |   503 |  2515 |     3   (0)| 00:00:01 |

-----------------------------------------------------------------------------

Predicate Information (identified by operation id):

---------------------------------------------------

   2 - filter(TO_NUMBER(:X)<=TO_NUMBER(:Y))

   3 - access("OBJECT_ID">=TO_NUMBER(:X) AND "OBJECT_ID"<=TO_NUMBER(:Y))

Statistics

----------------------------------------------------------

          0  recursive calls

          0  db block gets

        451  consistent gets

          0  physical reads

          0  redo size

        413  bytes sent via SQL*Net to client

        385  bytes received via SQL*Net from client

          2  SQL*Net roundtrips to/from client

          0  sorts (memory)

          0  sorts (disk)

          1  rows processed

從上面可以看出使用SET AUTOTRACE TRACEONLY後得到的執行計劃和之前用EXPLAIN PLAN指令得到的執行計劃是一樣的。即此時的SET AUTOTRACE TRACEONLY所得到的執行計劃是不準确的。

結論:通過上面的實驗可以證明使用了SET AUTOTRACE指令後顯示的執行計劃實際上是來源于調用EXPLAIN PLAN指令,而EXPLAIN PLAN指令所得到的執行計劃可能是不準确的(特别是在綁定變量的時候),索引使用SET AUTORACE指令的所顯示的執行計劃可能是不準确的。

ORACLE資料庫還有如下方法得到真實的執行計劃:

如果是ORACLE 10G及其以上的版本,該SQL的執行計劃又已經被ORACLE捕獲并存儲到了REPOSITORY中,在可以使用AWR SQL報告來得到真實的曆史執行計劃。