autotrace 和explain plan for可能導致執行計劃錯誤

wei-xh發表於2013-09-27
[i=s] 本帖最後由 wei-xh 於 2013-9-27 15:26 編輯

使用autotrace工具和explain plan for來獲得SQL的執行計劃,有可能會出現與實際的執行計劃不符的情況。autotrace本身其實也是呼叫了explain plan for來獲取的執行計劃。
本文總結了在使用這兩種工具過程中,最常見的導致執行計劃與實際情況不符的兩個場景。

第一種情況:資料型別隱式轉換

使用explain plan for工具時,要意識到ORACLE會把你傳入的繫結變數作為varchar2型別來處理。
create table wxh_tbd(id varchar2(100));
create index wxh_ind on wxh_tbd(id);
exec dbms_stats.gather_table_stats(user,'wxh_tbd');

SQL> var c number;
SQL> var d varchar2
SQL>
SQL> exec :c :=1

PL/SQL procedure successfully completed.

SQL> exec :d :='1'

PL/SQL procedure successfully completed.

SQL> set autotrace on
SQL>
SQL> alter session set events '10046 trace name context forever ,level 12' ;  

Session altered.

上面建立了一張表,表裡只有一個字元型別的欄位。宣告瞭兩個變數,一個為字元型,一個為數字型。
SQL> select count(*) from wxh_tbd where id= :d;

  COUNT(*)
----------
         1


Execution Plan
----------------------------------------------------------
Plan hash value: 3024061193

-----------------------------------------------------------------------------
| Id  | Operation         | Name    | Rows  | Bytes | Cost (%CPU)| Time     |
-----------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |         |     1 |     5 |     1   (0)| 00:00:01 |
|   1 |  SORT AGGREGATE   |         |     1 |     5 |            |          |
|*  2 |   INDEX RANGE SCAN| WXH_IND |     1 |     5 |     1   (0)| 00:00:01 |
-----------------------------------------------------------------------------

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

   2 - access("ID"=:D)


Statistics
----------------------------------------------------------
          1  recursive calls
          0  db block gets
          2  consistent gets
          0  physical reads
          0  redo size
        526  bytes sent via SQL*Net to client
        524  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processed



SQL> select count(*) from wxh_tbd where id= :c;

  COUNT(*)
----------
         1


Execution Plan
----------------------------------------------------------
Plan hash value: 3024061193

-----------------------------------------------------------------------------
| Id  | Operation         | Name    | Rows  | Bytes | Cost (%CPU)| Time     |
-----------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |         |     1 |     5 |     1   (0)| 00:00:01 |
|   1 |  SORT AGGREGATE   |         |     1 |     5 |            |          |
|*  2 |   INDEX RANGE SCAN| WXH_IND |     1 |     5 |     1   (0)| 00:00:01 |
-----------------------------------------------------------------------------

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

   2 - access("ID"=:C)


Statistics
----------------------------------------------------------
          1  recursive calls
          0  db block gets
         30  consistent gets
          0  physical reads
          0  redo size
        526  bytes sent via SQL*Net to client
        524  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processed

我們看到兩次查詢走的執行計劃是一樣的,都走了索引掃描,但是產生的邏輯讀差異是很大的,這裡面有貓膩!

雖然我們定義的c變數為number,但是explain plan for忽略了這個事實,僅僅把它作為varchar2來處理,因此執行計劃顯示的,還是走了索引掃描。
我們看看10046 trace的結果:
從跟蹤檔案中很容易看到set autotrace 本身呼叫了explain plan for來產生執行計劃。

EXPLAIN PLAN SET STATEMENT_ID='PLUS456426' FOR select count(*) from wxh_tbd
  where id= :d


EXPLAIN PLAN SET STATEMENT_ID='PLUS456426' FOR select count(*) from wxh_tbd
  where id= :c

跟蹤檔案裡清楚的記錄了,SQL的真實執行計劃為全表掃描,發生了資料型別的隱式轉換:
select count(*)
from
wxh_tbd where id= :c


call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        2      0.00       0.00          0          0          0           0
Execute      2      0.00       0.00          0          0          0           0
Fetch        4      0.00       0.00          0         60          0           2
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total        8      0.00       0.01          0         60          0           2

Misses in library cache during parse: 1
Misses in library cache during execute: 1
Optimizer mode: ALL_ROWS
Parsing user id: 45
Number of plan statistics captured: 2

Rows (1st) Rows (avg) Rows (max)  Row Source Operation
---------- ---------- ----------  ---------------------------------------------------
         1          1          1  SORT AGGREGATE (cr=30 pr=0 pw=0 time=2886 us)
         1          1          1   TABLE ACCESS FULL WXH_TBD (cr=30 pr=0 pw=0 time=2870 us cost=7 size=5 card=1)

第二種情況:繫結變數窺探,用explain plan for並不會受繫結變數窺探的影響,因此在列存在直方圖的情況下,有可能explain plan for產生的執行計劃於實際的執行計劃不符。
SQL> drop table a;

Table dropped.

SQL> create table a as select object_id,object_type from dba_objects;

Table created.

SQL> insert into a select  object_id,'TABLE' object_type from dba_objects;

17537 rows created.

SQL> commit;

Commit complete.

SQL> create index a_i on a(object_type);

Index created.

SQL> begin
  2    dbms_stats.gather_table_stats(ownname          =>'dlsp',
  3                                  tabname          => 'a',
  4                                  no_invalidate    => FALSE,
  5                                  estimate_percent => 100,
  6                                  force            => true,
  7                                  degree         => 5,
  8                                  method_opt       => 'for  columns object_type size 10',
  9                                  cascade          => false);
10  end;
11  /


SQL> @tabstat
Please enter Name of Table Owner: dlsp
Please enter Table Name : a

**********************************************************
Table Level
**********************************************************


Table                                  Number                        Empty    Chain Average Global         Sample Date
Name                                  of Rows          Blocks       Blocks    Count Row Len Stats            Size MM-DD-YYYY
------------------------------ -------------- --------------- ------------ -------- ------- ------ -------------- ----------
A                                      35,074            0,85            0        0      10 YES            35,074 09-27-2013

Column                             Distinct              Number       Number         Sample Date
Name                                 Values     Density Buckets        Nulls           Size MM-DD-YYYY
------------------------------ ------------ ----------- ------- ------------ -------------- ----------
OBJECT_ID                            17,536   .00005703       1            2         35,072 09-27-2013
OBJECT_TYPE                              38   .07672567      10            0         35,074 09-27-2013

Index                                     Leaf       Distinct         Number      AV       Av      Cluster Date
Name                           BL         Blks           Keys        of Rows     LEA     Data       Factor MM-DD-YYYY
------------------------------ -- ------------ -------------- -------------- ------- -------- ------------ ----------
A_I                             1           86             38         35,074       2       10          398 09-27-2013

Index                          Column                          Col Column
Name                           Name                            Pos Details
------------------------------ ------------------------------ ---- ------------------------
A_I                            OBJECT_TYPE                       1 VARCHAR2(19)



SQL> var c varchar2(100)
SQL>  exec :c := 'TABLE'

PL/SQL procedure successfully completed.

經過上面的程式碼,我們在object_type上產生了一個直方圖,並且object_type存在資料傾斜,表中存在大量的object_type為TABLE的值。


SQL> select count(*) from a where object_type=:c;

  COUNT(*)
----------
     19555

1 row selected.


Execution Plan
----------------------------------------------------------
Plan hash value: 3009055403

--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      |     1 |     7 |     3   (0)| 00:00:01 |
|   1 |  SORT AGGREGATE   |      |     1 |     7 |            |          |
|*  2 |   INDEX RANGE SCAN| A_I  |   923 |  6461 |     3   (0)| 00:00:01 |
--------------------------------------------------------------------------

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

   2 - access("OBJECT_TYPE"=:C)


Statistics
----------------------------------------------------------
          1  recursive calls
          0  db block gets
         83  consistent gets
          0  physical reads
          0  redo size
        528  bytes sent via SQL*Net to client
        524  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processed

獲得SQL_ID後,檢視真實的執行計劃。發現走的實際為全表掃描的執行計劃,還可以透過Peeked Binds部分看到此執行計劃窺探了實際的變數值

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

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
SQL_ID  7q143s0aqfm1q, child number 0
-------------------------------------
select count(*) from a where object_type=:c

Plan hash value: 2223038180

---------------------------------------------------------------------------
| Id  | Operation          | Name | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |      |       |       |    17 (100)|          |
|   1 |  SORT AGGREGATE    |      |     1 |     7 |            |          |
|*  2 |   TABLE ACCESS FULL| A    | 21044 |   143K|    17   (6)| 00:00:01 |
---------------------------------------------------------------------------



Outline Data

Peeked Binds (identified by position):
--------------------------------------

   1 - :C (VARCHAR2(30), CSID=871): 'TABLE'



來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/22034023/viewspace-773482/,如需轉載,請註明出處,否則將追究法律責任。

相關文章