【DB筆試面試597】在Oracle中,獲取執行計劃有哪幾種方法?

  • 2019 年 10 月 10 日
  • 筆記

題目部分

在Oracle中,獲取執行計劃有哪幾種方法?

答案部分

一般來說,有如下幾種獲取執行計劃的方式:

1、AUTOTRACE方式

AUTOTRACE是Oracle自帶的客戶端工具SQL*Plus的一個特性。啟用AUTOTRACE後,SQL*Plus會自動收集執行過的SQL語句的執行計劃、性能統計數據等,並在語句執行結束後顯示在SQL*Plus中。

DBA用戶可以直接使用AUTOTRACE功能,但是如果用戶沒有DBA許可權,那麼需要在SYS用戶下執行plustrce.sql腳本,自動創建PLUSTRACE角色,再把PLUSTRACE許可權賦給普通用戶即可。

$ORACLE_HOME/sqlplus/admin/plustrce.sql  GRANT PLUSTRACE TO USER_LHR;  

另外,若啟用AUTOTRACE報「SP2-0611」的錯誤,則可以執行utlxplan.sql腳本來創建表PLAN_TABLE,如下所示:

SQL> set autot on  SP2-0613: 無法驗證 PLAN_TABLE 格式或實體  SP2-0611: 啟用EXPLAIN報告時出錯  SQL> @?/rdbms/admin/utlxplan.sql  

在執行如下腳本後,每個用戶(包括以後新建的用戶)都可以使用AUTOTRACE命令:

@?/rdbms/admin/utlxplan.sql  CREATE PUBLIC SYNONYM PLAN_TABLE FOR PLAN_TABLE;  GRANT ALL ON PLAN_TABLE TO PUBLIC;  @?/sqlplus/admin/plustrce.sql  GRANT PLUSTRACE TO PUBLIC;  

AUTOTRACE的語法如下所示:

SET AUTOTRACE {OFF|ON|TRACEONLY} [EXPLAIN] [STATISTICS]  

其中,AUTOTRACE可簡寫為AUTOT,TRACEONLY可簡寫為TRACE,EXPLAIN可簡寫為EXP,STATISTICS可簡寫為STAT。

SQL> SET AUTOT ON  SQL> SELECT COUNT(*) FROM PLAN_TABLE;    COUNT(*)  ----------          68  Execution Plan  ----------------------------------------------------------  Plan hash value: 1751138260  --------------------------------------------------------------------------  | Id  | Operation          | Name        | Rows  | Cost (%CPU)| Time     |  --------------------------------------------------------------------------  |   0 | SELECT STATEMENT   |             |     1 |     3   (0)| 00:00:01 |  |   1 |  SORT AGGREGATE    |             |     1 |            |          |  |   2 |   TABLE ACCESS FULL| PLAN_TABLE$ |    68 |     3   (0)| 00:00:01 |  --------------------------------------------------------------------------  Note  -----     - dynamic sampling used for this statement  Statistics  ----------------------------------------------------------           27  recursive calls            0  db block gets           15  consistent gets            0  physical reads            0  redo size          515  bytes sent via SQL*Net to client          487  bytes received via SQL*Net from client            2  SQL*Net roundtrips to/from client            0  sorts (memory)            0  sorts (disk)            1  rows processed  

AUTOTRACE STATISTICS含義見下表:

序號

列名

解釋

1

recursive calls

遞歸調用,表示執行SQL的時候的產生的遞歸調用的次數。Oracle在執行SQL的時候,有時候會生成很多額外的SQL語句,這個就稱為遞歸調用。這個參數和訪問數據字典的次數有很大的關係,一般來說,這個參數值不會很大。

2

db block gets

DB塊取,表示當前讀。在發生INSERT、DELETE、UPDATE和SELECT FOR UPDATE的時候,資料庫緩衝區中的資料庫塊的個數。在SELECT語句中一般為0。

3

consistent gets

一致性讀,表示除了SELECT FOR UPDATE的時候,從資料庫緩衝區中讀取的數據塊的個數(注意,實際上並不是塊的個數),可能會讀取回滾段的資訊,一般來說,邏輯讀(Logical Reads) = 當前讀(db block gets) + 一致性讀(consistent gets)。

4

physical reads

物理讀,在執行SQL的過程中,從硬碟上讀取的數據塊個數。

5

redo size

SQL語句在執行過程中產生的Redo的位元組數。

6

bytes sent via SQL*Net to client

伺服器利用SQL*Net發送到客戶端的位元組數。

7

bytes received via SQL*Net from client

伺服器利用SQL*Net從客戶端接收的位元組數。

8

SQL*Net roundtrips to/from client

從客戶端發送和接收的SQL*Net消息的總數,包括從多行的結果集中提取的往返消息。

9

sorts (memory)

在記憶體執行的排序次數。

10

sorts (disk)

在磁碟上執行的排序次數,如果記憶體空間不足,那麼會使用磁碟空間。

11

rows processed

更改或選擇返回的行數。

2、EXPLAIN PLAN FOR方式

SQL> EXPLAIN PLAN FOR SELECT * FROM T017_LHRO;  SQL> SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY);  PLAN_TABLE_OUTPUT  --------------------------------------------------------------------------------  Plan hash value: 3200443156  -------------------------------------------------------------------------------  | Id  | Operation         | Name      | Rows  | Bytes | Cost (%CPU)| Time     |  -------------------------------------------------------------------------------  |   0 | SELECT STATEMENT  |           |  1363 |   177K|     9   (0)| 00:00:01 |  |   1 |  TABLE ACCESS FULL| T017_LHRO |  1363 |   177K|     9   (0)| 00:00:01 |  -------------------------------------------------------------------------------  

3、DBMS_XPLAN.DISPLAY_CURSOR方式

SYS@RAC2LHR1> SELECT * FROM V$VERSION WHERE ROWNUM<2;  BANNER  --------------------------------------------------------------------------------  Oracle Database 11g Enterprise Edition Release 11.2.0.4.0 - 64bit Production  SYS@RAC2LHR1> SELECT ENAME,DNAME,LOC    2   FROM SCOTT.EMP E,SCOTT.DEPT D    3  WHERE E.DEPTNO = D.DEPTNO    4  AND E.EMPNO = 7788;  ENAME      DNAME          LOC  ---------- -------------- -------------  SCOTT      RESEARCH       DALLAS  

如果不傳遞任何參數給DISPLAY_CURSOR函數,那麼默認顯示當前會話最後一條SQL語句的執行計劃,如下所示:

SYS@RAC2LHR1> SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR(NULL,NULL));  PLAN_TABLE_OUTPUT  -----------------------------------------------------------------------------  SQL_ID  315xan8zgvtbm, child number 0  -------------------------------------  SELECT ENAME,DNAME,LOC  FROM SCOTT.EMP E,SCOTT.DEPT D WHERE E.DEPTNO =  D.DEPTNO AND E.EMPNO = 7788  Plan hash value: 1674520956  ----------------------------------------------------------------------------------------  | Id  | Operation                    | Name    | Rows  | Bytes | Cost (%CPU)| Time     |  ----------------------------------------------------------------------------------------  |   0 | SELECT STATEMENT             |         |       |       |     2 (100)|          |  |   1 |  NESTED LOOPS                |         |     1 |    33 |     2   (0)| 00:00:01 |  |   2 |   TABLE ACCESS BY INDEX ROWID| EMP     |     1 |    13 |     1   (0)| 00:00:01 |  |*  3 |    INDEX UNIQUE SCAN         | PK_EMP  |     1 |       |     0   (0)|          |  |   4 |   TABLE ACCESS BY INDEX ROWID| DEPT    |     1 |    20 |     1   (0)| 00:00:01 |  |*  5 |    INDEX UNIQUE SCAN         | PK_DEPT |     1 |       |     0   (0)|          |  ----------------------------------------------------------------------------------------  Predicate Information (identified by operation id):  ---------------------------------------------------     3 - access("E"."EMPNO"=7788)     5 - access("E"."DEPTNO"="D"."DEPTNO")  24 rows selected.  

傳遞SQL_ID以及FORMAT參數給DISPLAY_CURSOR函數,並配合修飾符控制執行計劃的輸出,如下所示:

SYS@RAC2LHR1> SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR('315XAN8ZGVTBM',NULL,'ALL'));  PLAN_TABLE_OUTPUT  ----------------------------------------------------------------------------  SQL_ID  315xan8zgvtbm, child number 0  -------------------------------------  SELECT ENAME,DNAME,LOC  FROM SCOTT.EMP E,SCOTT.DEPT D WHERE E.DEPTNO =  D.DEPTNO AND E.EMPNO = 7788  Plan hash value: 1674520956  ----------------------------------------------------------------------------------------  | Id  | Operation                    | Name    | Rows  | Bytes | Cost (%CPU)| Time     |  ----------------------------------------------------------------------------------------  |   0 | SELECT STATEMENT             |         |       |       |     2 (100)|          |  |   1 |  NESTED LOOPS                |         |     1 |    33 |     2   (0)| 00:00:01 |  |   2 |   TABLE ACCESS BY INDEX ROWID| EMP     |     1 |    13 |     1   (0)| 00:00:01 |  |*  3 |    INDEX UNIQUE SCAN         | PK_EMP  |     1 |       |     0   (0)|          |  |   4 |   TABLE ACCESS BY INDEX ROWID| DEPT    |     1 |    20 |     1   (0)| 00:00:01 |  |*  5 |    INDEX UNIQUE SCAN         | PK_DEPT |     1 |       |     0   (0)|          |  ----------------------------------------------------------------------------------------  Query Block Name / Object Alias (identified by operation id):  -------------------------------------------------------------     1 - SEL$1     2 - SEL$1 / E@SEL$1     3 - SEL$1 / E@SEL$1     4 - SEL$1 / D@SEL$1     5 - SEL$1 / D@SEL$1  Predicate Information (identified by operation id):  ---------------------------------------------------     3 - access("E"."EMPNO"=7788)     5 - access("E"."DEPTNO"="D"."DEPTNO")  Column Projection Information (identified by operation id):  -----------------------------------------------------------     1 - "ENAME"[VARCHAR2,10], "DNAME"[VARCHAR2,14], "LOC"[VARCHAR2,13]     2 - "ENAME"[VARCHAR2,10], "E"."DEPTNO"[NUMBER,22]     3 - "E".ROWID[ROWID,10]     4 - "DNAME"[VARCHAR2,14], "LOC"[VARCHAR2,13]     5 - "D".ROWID[ROWID,10]  42 rows selected.  

利用STATISTICS_LEVEL或/*+ GATHER_PLAN_STATISTICS*/可以知道表訪問的次數,也可以查看真實執行計劃並獲得統計資訊。如下所示:

SET SERVEROUTPUT OFF  ALTER SESSION SET STATISTICS_LEVEL=ALL;  執行SQL語句  SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR(NULL,NULL,'ADVANCED ALLSTATS LAST'));  SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR(SQL_ID =>'',CURSOR_CHILD_NO =>1,FORMAT => 'ADVANCED ALLSTATS'));  

其中參數SQL_ID為父游標,如果為NULL,那麼表示顯示該會話之前的SQL執行計劃。CURSOR_CHILD_NO為子游標的序號,默認為0,如果設定為NULL,那麼所有該父游標下所有的子游標的執行計劃都將返回。參數FORMAT指定要顯示哪些資訊,常用的有:IOSTATS(I/O資訊顯示)、ALLSTATS(I/O資訊顯示+PGA資訊)、ADVANCED(顯示所有統計資訊)、IOSTATS LAST或ALLSTATS LAST(只顯示最後一次執行的統計資訊)。默認值TYPICAL只能顯示一個普通的執行計劃,不能顯示出實際返回的行。

這種方式也是SQL調優中常用的方法,但使用該方法的前提是如下兩個條件必須同時滿足:

① 一般在會話級別設置參數STATISTICS_LEVEL為ALL,也可以使用/*+ GATHER_PLAN_STATISTICS*/提示。

② 若DBMS_XPLAN.DISPLAY_CURSOR中的入參SQL_ID輸入值為NULL的話,則SERVEROUTPUT必須設置為OFF(SET SERVEROUTPUT OFF),否則會報類似如下的錯誤:

PLAN_TABLE_OUTPUT  --------------------------------------------------------------------------------  SQL_ID  9m7787camwh4m, child number 0  begin :id := sys.dbms_transaction.local_transaction_id; end;  NOTE: cannot fetch plan for SQL_ID: 9m7787camwh4m, CHILD_NUMBER: 0        Please verify value of SQL_ID and CHILD_NUMBER;        It could also be that the plan is no longer in cursor cache (check v$sql_plan)  

若為具體SQL_ID的值的話,則無論SERVEROUTPUT的值如何都可以正常執行。

示例如下所示:

SYS@RAC2LHR1> SHOW PARAMETER STATISTICS_LEVEL  NAME                                 TYPE        VALUE  ------------------------------------ ----------- ------------------------------  statistics_level                     string      TYPICAL  SYS@RAC2LHR1> ALTER SESSION SET STATISTICS_LEVEL=ALL;  Session altered.  SYS@RAC2LHR1> SHOW SERVEROUTPUT  serveroutput OFF  SYS@RAC2LHR1> SELECT ENAME,DNAME,LOC    2   FROM SCOTT.EMP E,SCOTT.DEPT D    3   WHERE E.DEPTNO = D.DEPTNO    4    AND E.EMPNO = 7369;  ENAME      DNAME          LOC  ---------- -------------- -------------  SMITH      RESEARCH       DALLAS  SYS@RAC2LHR1> SET PAGESIZE 0  SYS@RAC2LHR1> SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR(NULL,NULL,'IOSTATS LAST -PREDICATE -NOTE'));  SQL_ID  g3mx9hdyrhus7, child number 0  -------------------------------------  SELECT ENAME,DNAME,LOC  FROM SCOTT.EMP E,SCOTT.DEPT D  WHERE E.DEPTNO =  D.DEPTNO   AND E.EMPNO = 7369  Plan hash value: 1674520956  --------------------------------------------------------------------------------------------------  | Id  | Operation                    | Name    | Starts | E-Rows | A-Rows |   A-Time   | Buffers |  --------------------------------------------------------------------------------------------------  |   0 | SELECT STATEMENT             |         |      1 |        |      1 |00:00:00.01 |       4 |  |   1 |  NESTED LOOPS                |         |      1 |      1 |      1 |00:00:00.01 |       4 |  |   2 |   TABLE ACCESS BY INDEX ROWID| EMP     |      1 |      1 |      1 |00:00:00.01 |       2 |  |   3 |    INDEX UNIQUE SCAN         | PK_EMP  |      1 |      1 |      1 |00:00:00.01 |       1 |  |   4 |   TABLE ACCESS BY INDEX ROWID| DEPT    |      1 |      1 |      1 |00:00:00.01 |       2 |  |   5 |    INDEX UNIQUE SCAN         | PK_DEPT |      1 |      1 |      1 |00:00:00.01 |       1 |  --------------------------------------------------------------------------------------------------  SYS@RAC2LHR1> SELECT /*+ GATHER_PLAN_STATISTICS */ ENAME,DNAME,LOC    2   FROM SCOTT.EMP E,SCOTT.DEPT D    3   WHERE E.DEPTNO = D.DEPTNO    4   AND E.EMPNO = 7369;  SMITH      RESEARCH       DALLAS  SYS@RAC2LHR1> SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR(NULL,NULL,'BASIC LAST ALLSTATS'));  EXPLAINED SQL STATEMENT:  ------------------------  SELECT /*+ GATHER_PLAN_STATISTICS */ ENAME,DNAME,LOC  FROM SCOTT.EMP  E,SCOTT.DEPT D  WHERE E.DEPTNO = D.DEPTNO  AND E.EMPNO = 7369  Plan hash value: 1674520956  --------------------------------------------------------------------------------------------------  | Id  | Operation                    | Name    | Starts | E-Rows | A-Rows |   A-Time   | Buffers |  --------------------------------------------------------------------------------------------------  |   0 | SELECT STATEMENT             |         |      1 |        |      1 |00:00:00.01 |       4 |  |   1 |  NESTED LOOPS                |         |      1 |      1 |      1 |00:00:00.01 |       4 |  |   2 |   TABLE ACCESS BY INDEX ROWID| EMP     |      1 |      1 |      1 |00:00:00.01 |       2 |  |*  3 |    INDEX UNIQUE SCAN         | PK_EMP  |      1 |      1 |      1 |00:00:00.01 |       1 |  |   4 |   TABLE ACCESS BY INDEX ROWID| DEPT    |      1 |      1 |      1 |00:00:00.01 |       2 |  |*  5 |    INDEX UNIQUE SCAN         | PK_DEPT |      1 |      1 |      1 |00:00:00.01 |       1 |  --------------------------------------------------------------------------------------------------  Predicate Information (identified by operation id):  ---------------------------------------------------     3 - access("E"."EMPNO"=7369)     5 - access("E"."DEPTNO"="D"."DEPTNO")  

4、其它跟蹤方法

除了上述方法外,還可以通過其它一些途徑獲取到語句的執行計劃,例如10046,10053事件等,但在這些方法所產生的數據里,執行計劃通常僅是輔助解決問題的一個部分,而非重點。

5、第三方工具

利用第三方工具,如PL/SQL DEV、TODO等開發工具,在PL/SQL DEV中選定SQL後,按F5即可查看執行計劃:

此外,還可以通過寫腳本從V$SQL_PLAN、DBA_HIST_SQL_PLAN、V$SQL_PLAN_MONITOR等視圖中來獲取執行計劃。

下表對這幾種獲取執行計劃的方法給予總結:

方法

簡介

SQL語句是否真實執行過

是否真實執行計劃

物理讀、邏輯讀、遞歸調用

運行時間

處理行數

表訪問次數

等待事件

解析時間

set autotrace

SET AUTOTRACE OFF

此為默認值,即關閉AUTOTRACE

SET AUTOTRACE ON

包含SQL語句的執行結果、SQL語句執行結果的數量、執行計劃和統計資訊內容

不確定

SET AUTOTRACE ON EXPLAIN

包含SQL語句的執行結果、SQL語句執行結果的數量和執行計劃

SET AUTOTRACE ON STATISTICS

包含SQL語句的執行結果、SQL語句執行結果的數量和統計資訊內容

SET AUTOTRACE TRACEONLY

包含SQL執行結果的數量、執行計劃和統計資訊內容,但不顯示SQL語句的執行結果

SET AUTOTRACE TRACEONLY EXPLAIN

同EXPLAIN PLAN命令,對於SELECT語句不會執行,只顯示目標SQL的執行計劃,但是對於DML語句還是會執行的,而且顯示SQL語句執行結果的數量和執行計劃

SET AUTOTRACE TRACEONLY STATISTICS

顯示SQL語句執行結果的數量和統計資訊,不顯示執行計劃和SQL執行結果

DBMS_XPLAN

EXPLAIN PLAN FORDBMS_XPLAN.DISPLAY

EXPLAIN PLAN FOR SQL語句;SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY);SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY(NULL,NULL,'ADVANCED'));

不確定

STATISTICS_LEVEL=ALLSELECT /*+ GATHER_PLAN_STATISTICS*/ …DBMS_XPLAN.DISPLAY_CURSOR

ALTER SESSION SET STATISTICS_LEVEL=ALL ;執行SQLSELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR(NULL,NULL,'ALLSTATS LAST'));SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR('&SQLID',0,'BASIC LAST ALLSTATS ADVANCED'));

DBMS_XPLAN.DISPLAY_CURSOR

沒有設置STATISTICS_LEVEL=ALL或沒有使用/*+ GATHER_PLAN_STATISTICS*/的Hint:SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR('&SQLID')); –從記憶體得到執行計劃

DBMS_XPLAN.DISPLAY_AWR

DISPLAY_AWR函數顯示存儲在AWR歷史數據的執行計劃。SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_AWR('&SQLID'));

DBMS_XPLAN.DISPLAY_SQLSET

DISPLAY_SQLSET函數顯示存儲在一個SQL調優集中的語句的執行計劃,SQL調優集查詢DBA_SQLSET_STATEMENTS,查詢執行計劃的SQL語句為:SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_SQLSET('&SQLSET','&SQLID',NULL,'BASIC ALLSTATS ADVANCED'));

DBMS_XPLAN.DISPLAY_SQL_PLAN_BASELINE

DISPLAY_SQL_PLAN_BASELINE函數顯示存儲在數據字典當中SQL執行計劃基準線的計劃。執行計劃基準線所屬SQL的句柄名稱(SQL_HANDLE)可以通過視圖DBA_SQL_PLAN_BASELINES查詢,查詢執行計劃的SQL語句為:SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_SQL_PLAN_BASELINE(SQL_HANDLE => ));

SQL_TRACE、事件10046、10053跟蹤

ALTER SESSION SET EVENTS '10046 TRACE NAME CONTEXT FOREVER,LEVEL 12'; 執行SQL語句ALTER SESSION SET EVENTS '10046 TRACE NAME CONTEXT OFF';TKPROF格式化TRACE文件

awrsqrpt.sql

@?/rdbms/admin/awrsqrpt.sqlSELECT OUTPUT FROM TABLE(DBMS_WORKLOAD_REPOSITORY.AWR_SQL_REPORT_HTML(V_DBID,V_INST_ID,V_MIN_SNAP_ID,V_MAX_SNAP_ID,V_SQLID));

SQL實時監控特性:DBMS_SQLTUNE.REPORT_SQL_MONITOR

SELECT DBMS_SQLTUNE.REPORT_SQL_MONITOR('&SQLID') FROM DUAL ;SELECT DBMS_SQLTUNE.REPORT_SQL_MONITOR(SQL_ID=>'&SQLID',TYPE=>'ACTIVE',REPORT_LEVEL=>'ALL') AS REPORT FROM DUAL;

其它工具

利用第三方工具,如PL/SQL DEV、TODO等開發工具,在PL/SQL DEV中選定SQL後,按F5即可查看執行計劃

不確定

對於這幾種獲取執行計劃的方法有如下結論:

① 若目標SQL需要執行很長時間才能返回結果,則推薦使用EXPLAIN PLAN FOR來獲取執行計劃。

② 若要查詢目標SQL的所有子游標的執行計劃,則推薦使用DBMS_XPLAN.DISPLAY_CURSOR('&SQLID', NULL,'ADVANCED ALLSTATS')或awrsqrpt.sql來獲取執行計劃。

③ 若要分析SQL語句的內部調用詳情,則推薦使用10046事件。

④ 若想確保看到真實的執行計劃,則不能使用EXPLAIN PLAN FOR和SET AUTOTRACE TRACEONLY EXPLAIN。

⑤ 若想獲取到表的訪問次數,則推薦/*+ GATHER_PLAN_STATISTICS*/。

⑥ 若資料庫版本大於10g,則對執行時間較長的SQL語句推薦使用SQL實時監控特性查看html報告。

& 說明:

有關Oracle查看執行計劃的幾種方法的更多內容可以參考我的BLOG:http://blog.itpub.net/26736162/viewspace-2136865/

本文選自《Oracle程式設計師面試筆試寶典》,作者:李華榮。