4.1 读懂执行计划

什么是执行计划(EXPLAIN PLAN)?通俗来讲,执行计划就是Oracle基于成本(cost)算法和统计信息,最终得到资源消耗最低的SQL执行步骤(或执行步骤的组合)。其中,成本的值是一个估算值,包括访问路径、关联方式、I/O、CPU和内存等。基于成本计算执行计划贯穿于Oracle SQL优化的始终,读懂执行计划是开始SQL优化的第一步,它的重要性再怎么强调也不为过。

4.1.1 获取执行计划

1.EXPLAIN PLAN

优化器将基于当前数据库对象统计信息生成执行计划和相关信息,而不实际执行EXPLAIN PLAN后面的语句。EXPLAIN PLAN的使用语法如图4-1所示。

图4-1 EXPLAIN PLAN使用语法

语法中的部分参数说明如下。

·STATEMENT_ID:用于区分PLAN TABLE中不同SQL语句的标识符,默认值为NULL。

·INTO table:用于指定输出表的名称,还可以指定用户和数据库,若未指定则输出到当前用户的PLAN TABLE下,建议保持默认设置,也就是不指定。

·FOR STATEMENT:指定为其生成执行计划的子句。支持DML和部分DDL(如MERGE、CREATE TABLE、CREATE INDEX和ALTER INDEX、REBUILD等)。

PLAN_TABLE只是一个会话级的临时表,其所生成的数据仅为当前会话所特有。

使用PLAN TABLE查看执行计划,指定STATEMENT_ID为'JASON PLAN T1',命令如下:


SQL> EXPLAIN PLAN 
SET STATEMENT_ID = 'JASON PLAN T1' 
INTO plan_table
    FOR select e.empno, e.ename, e.job, d.dname
  from scott.emp e, scott.dept d
 where e.deptno = d.deptno
   and d.dname = 'ACCOUNTING';

有如下两种方式可以查看PLAN TABLE中返回的执行计划和成本。

1)使用SELECT语句查询,命令如下:


SQL> SELECT id,
       LPAD(' ', 2 * (LEVEL - 1)) || operation operation,
       options,
       object_name,
       cost,
       object_alias,
       position
  FROM plan_table
 START WITH id = 0
        AND statement_id = 'JASON PLAN T1'
CONNECT BY PRIOR id = parent_id
       AND statement_id = 'JASON PLAN T1'
 ORDER BY id;

使用SELECT语句查询的结果如图4-2所示。

图4-2 使用SELECT语句查询输出信息

2)使用DBMS_XPLAN中的DISPLAY函数查看,命令如下:


DBMS_XPLAN.DISPLAY(
table_name    IN   VARCHAR2 DEFAULT 'PLAN_TABLE', 
statement_id  IN   VARCHAR2 DEFAULT NULL, 
format        IN   VARCHAR2 DEFAULT 'TYPICAL', 
filter_preds  IN   VARCHAR2 DEFAULT NULL);

SQL> select * from table(dbms_xplan.display(NULL,'JASON PLAN T1',NULL));
Plan hash value: 844388907
-------------------------------------------------------------------------------------
| Id | Operation                    | Name    | Rows | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------------
|  0 | SELECT STATEMENT             |         |  5   |  170  |     6  (17)| 00:00:01 |
|  1 |  MERGE JOIN                  |         |  5   |  170  |     6  (17)| 00:00:01 |
|* 2 |   TABLE ACCESS BY INDEX ROWID| DEPT    |  1   |  13   |     2   (0)| 00:00:01 |
|  3 |    INDEX FULL SCAN           | PK_DEPT |  4   |       |     1   (0)| 00:00:01 |
|* 4 |   SORT JOIN                  |         |  14  |  294  |     4  (25)| 00:00:01 |
|  5 |    TABLE ACCESS FULL         | EMP     |  14  |  294  |     3   (0)| 00:00:01 |
-------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   2 - filter("D"."DNAME"='ACCOUNTING')
   4 - access("E"."DEPTNO"="D"."DEPTNO")
       filter("E"."DEPTNO"="D"."DEPTNO")

如需要查询更详细的信息,例如,Query Block Name / Object Alias、Column Projection Information,则可以添加ADVANCED关键字,命令如下:


SQL> select * from table(dbms_xplan.display(NULL,'JASON PLAN T1','ADVANCED'));
…………
Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------
   1 - SEL$1
   2 - SEL$1 / D@SEL$1
   3 - SEL$1 / D@SEL$1
   5 - SEL$1 / E@SEL$1
Outline Data
-------------
  /*+
      BEGIN_OUTLINE_DATA
      PX_JOIN_FILTER(@"SEL$1" "E"@"SEL$1")
      USE_MERGE(@"SEL$1" "E"@"SEL$1")
      LEADING(@"SEL$1" "D"@"SEL$1" "E"@"SEL$1")
      FULL(@"SEL$1" "E"@"SEL$1")
      INDEX(@"SEL$1" "D"@"SEL$1" ("DEPT"."DEPTNO"))
      OUTLINE_LEAF(@"SEL$1")
      ALL_ROWS
      DB_VERSION('11.2.0.4')
      OPTIMIZER_FEATURES_ENABLE('11.2.0.4')
      IGNORE_OPTIM_EMBEDDED_HINTS
      END_OUTLINE_DATA
  */
Predicate Information (identified by operation id):
---------------------------------------------------
   2 - filter("D"."DNAME"='ACCOUNTING')
   4 - access("E"."DEPTNO"="D"."DEPTNO")
       filter("E"."DEPTNO"="D"."DEPTNO")
Column Projection Information (identified by operation id):
-----------------------------------------------------------
   1 - (#keys=0) "D"."DNAME"[VARCHAR2,14], "E"."EMPNO"[NUMBER,22],
       "E"."ENAME"[VARCHAR2,10], "E"."JOB"[VARCHAR2,9]
   2 - "D"."DEPTNO"[NUMBER,22], "D"."DNAME"[VARCHAR2,14]
   3 - "D".ROWID[ROWID,10], "D"."DEPTNO"[NUMBER,22]
   4 - (#keys=1) "E"."DEPTNO"[NUMBER,22], "E"."EMPNO"[NUMBER,22],
       "E"."ENAME"[VARCHAR2,10], "E"."JOB"[VARCHAR2,9]
   5 - "E"."EMPNO"[NUMBER,22], "E"."ENAME"[VARCHAR2,10], "E"."JOB"[VARCHAR2,9],
       "E"."DEPTNO"[NUMBER,22]

代码段中的部分参数说明如下。

·Query Block Name / Object Alias:表示查询块名称/对象别名,SQL语句中每一个子查询都称为Query Block Name(查询块名称),子查询中涉及的对象称为Object Alias(对象别名)。它们主要是为了避免SQL语句中包含多个子查询,而每个子查询又都涉及相同的对象,比如同一张表,为了进行区分,Oracle为子查询中的每一个对象分别取了不同的别名,以用于区分。

·Outline Data:表示SQL语句内部的HINT(提示)信息。

·Predicate Information:表示谓词信息,一般是指对象的连接和访问方式。

·Column Projection Information:表示SQL语句相关列信息。

2.SET AUTOTRACE

同样,优化器将基于当前统计信息生成执行计划和相关信息,而不实际执行此语句,SET AUTOTRACE的使用方法如下:


Usage: SET AUTOT[RACE] {OFF | ON | TRACE[ONLY]} [EXP[LAIN]] [STAT[ISTICS]]

ON和TRACE的区别在于后者不产生SQL的输出结果;EXP和STAT的区别是,前者只看执行计划,后者只看统计信息。

笔者常用的方法为SET AUTOTRACE TRACEONLY,示例代码如下:


SQL> set autot traceonly
SQL> select c.cust_first_name, c.cust_last_name, p.prod_name, s.amount_sold
  2    from sh.customers c, sh.sales s, sh.products p
  3   where c.cust_id = s.cust_id
  4     and p.prod_id = s.prod_id
  5     and c.cust_id = 987;
…………
Predicate Information (identified by operation id):
---------------------------------------------------
   1 - access("C"."CUST_ID"="S"."CUST_ID" AND "P"."PROD_ID"="S"."PROD_ID")
   4 - access("C"."CUST_ID"=987)
   9 - access("S"."CUST_ID"=987)
Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
        176  consistent gets
          0  physical reads
          0  redo size
       7801  bytes sent via SQL*Net to client
        645  bytes received via SQL*Net from client
         13  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
        180  rows processed

Predicate Information(谓词信息)输出的信息与使用EXPLAIN PLAN获取的内容类似,区别在于SET AUTOTRACE会在执行的最后输出该SQL语句的Statistics(统计)信息,部分解释如下。

·recursive calls:表示递归调用的次数。

·db block gets:表示发生变化的数据块个数,单位是块,一般情况下,只有在DML中才会有具体的值。

·consistent gets:表示逻辑读个数,单位是块。

·physical reads:表示物理读个数,单位是块。

·redo size:表示SQL语句产生的重做日志的大小,单位是字节。

·bytes sent via SQL*Net to client:表示数据库发送到客户端的字节大小,单位是字节。

·bytes received via SQL*Net from client:表示客户端发送到数据库的字节大小,单位是字节。

·SQL*Net roundtrips to/from client:表示客户端和数据库服务器来回往返的次数。

·sorts (memory/disk):表示内存或磁盘排序的次数。

·rows processed:表示SQL语句处理的行数。

3.DBMS_XPLAN

DBMS_XPLAN是笔者最推荐同时也是最准确的执行计划查看方式。

1)DISPLAY_CURSOR:用于获取内存中shared_pool游标缓存。示例代码如下:


DBMS_XPLAN.DISPLAY_CURSOR
FUNCTION DISPLAY_CURSOR RETURNS DBMS_XPLAN_TYPE_TABLE
 Argument Name                  Type                    In/Out Default?
 ------------------------------ ----------------------- ------ --------
 SQL_ID                         VARCHAR2                IN     DEFAULT
 CURSOR_CHILD_NO                NUMBER(38)              IN     DEFAULT
 FORMAT                         VARCHAR2                IN     DEFAULT
SQL> select * from table(dbms_xplan.DISPLAY_CURSOR('&SQL_ID', null, 'ADVANCED 
    ALLSTATS LAST'));

其中涉及的参数有SQL_ID、CURSOR_CHILD_NO和FORMAT,具体说明如下。

·SQL_ID:表示对应的SQL语句在v$sql中匹配的SQL_ID。


SQL> select sql_id,sql_text, CHILD_NUMBER from v$sql where sql_text like '%SQL%';

·CURSOR_CHILD_NO:表示对应的SQL语句在v$sql中匹配的CHILD_NUMBER,若输入NULL则表示显示所有的子游标。

·FORMAT:表示控制执行计划输出的详细程度,FORMAT的参数和组合比较多,具体请参考《PL/SQL Packages and Types Reference》中DBMS_XPLAN的使用方法。较为常用的组合如下。

·ALLSTATS LAST:ALLSTATS表示获取Starts(对象访问次数)、A-Rows(实际行数)、A-Time(实际时间)等实际信息,LAST表示基于最后一次统计信息的执行计划。

·ADVANCED ALLSTATS LAST:在ALLSTATS LAST的基础上多出了ADVANCED参数,作用是显示Query Block Name / Object Alias、Outline Data、Column Projection Information,与第1节EXPLAIN PLAN中的用法相似。

2)DISPLAY_AWR:用于获取AWR基表WRH$_SQL_PLAN。示例代码如下:


DBMS_XPLAN.DISPLAY_AWR
FUNCTION DISPLAY_AWR RETURNS DBMS_XPLAN_TYPE_TABLE
 Argument Name                  Type                    In/Out Default?
 ------------------------------ ----------------------- ------ --------
 SQL_ID                         VARCHAR2                IN
 PLAN_HASH_VALUE                NUMBER(38)              IN     DEFAULT
 DB_ID                          NUMBER(38)              IN     DEFAULT
 FORMAT                         VARCHAR2                IN     DEFAULT
SQL> select * from table(dbms_xplan.display_awr('&SQL_ID',null,null,'ADVANCED '));

其中涉及的参数有SQL_ID、PLAN_HASH_VALUE、DB_ID和FORMAT,具体说明如下。

·PLAN_HASH_VALUE:表示对应的SQL语句在v$sql_plan中匹配的PLAN_HASH_VALUE,若输入NULL则显示该语句所有的执行计划。

·DB_ID:默认获取本地v$database中的DATABASE_ID。

执行计划是SQL语句执行前基于当前的统计信息生成的,其中,ROWS、Bytes、Cost、Time等为评估值,为了获取更为准确的实际值,即A-Rows、A-Time等,数据库在执行SQL语句时需要做额外的收集。

为执行计划获取准确的实际值的方法如下。

·会话级:“alter session set STATISTICS_LEVEL=ALL;”,默认设置为TYPICAL。

·语句级:“select /*+gather_plan_statistics */……”需要在SQL语句中添加HINT。

推荐会话级设置,执行完恢复默认设置TYPICAL,由于额外收集需要消耗更多的系统资源。因此不建议系统全局开启,仅在SQL调优时使用。

接下来就为大家演示具体的使用方法,代码如下:


SQL> alter session set STATISTICS_LEVEL = ALL;
SQL> select * from scott.dept d where d.deptno in (select e.deptno from scott.emp e); 
SQL> select * from table(dbms_xplan.DISPLAY_CURSOR(null, null, 'ADVANCED ALLSTATS LAST'));   
        10 ACCOUNTING     NEW YORK
        20 RESEARCH       DALLAS
        30 SALES          CHICAGO

SQL_ID  1w5h64k1fckmr, child number 1
-------------------------------------
select * from scott.dept d where d.deptno in (select e.deptno from
scott.emp e)

Plan hash value: 1090737117
<!--省略(Name、E-Rows、E-Bytes和E-Time)部分列-->
-----------------------------------------------------------------------------------------------------
| Id | Operation | Starts | Cost (%CPU)| A-Rows | A-Time | Buffers    | OMem | 1Mem | Used-Mem  |
-------------------------------------------------------------------------------------------------
|  0 | SELECT STATEMENT   | 1          | 6 (100)| 3      |00:00:00.01 |   10 |      |    |      |
|  1 |  MERGE JOIN SEMI   | 1          | 6  (17)| 3      |00:00:00.01 |   10 |      |    |      |
|  2 |   TABLE ACCESS BY  | 1          | 2   (0)| 4      |00:00:00.01 |    4 |      |    |      |
             INDEX ROWID
|  3 |    INDEX FULL SCAN | 1          | 1   (0)| 4      |00:00:00.01 |    2 |      |    |      |
|* 4 |   SORT UNIQUE      | 4          | 4  (25)| 3      |00:00:00.01 |    6 | 2048 |2048|2048(0)|
|  5 |    TABLE ACCESS FULL| 1          | 3   (0)| 14     |00:00:00.01 |    6 |      |    |      |
-------------------------------------------------------------------------------------------------
Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------
   1 - SEL$5DA710D3
   2 - SEL$5DA710D3 / D@SEL$1
   3 - SEL$5DA710D3 / D@SEL$1
   5 - SEL$5DA710D3 / E@SEL$2
Outline Data
-------------
  /*+
      BEGIN_OUTLINE_DATA
      IGNORE_OPTIM_EMBEDDED_HINTS
      OPTIMIZER_FEATURES_ENABLE('11.2.0.4')
      DB_VERSION('11.2.0.4')
      ALL_ROWS
      OUTLINE_LEAF(@"SEL$5DA710D3")
      UNNEST(@"SEL$2")
      OUTLINE(@"SEL$1")
      OUTLINE(@"SEL$2")
      INDEX(@"SEL$5DA710D3" "D"@"SEL$1" ("DEPT"."DEPTNO"))
      FULL(@"SEL$5DA710D3" "E"@"SEL$2")
      LEADING(@"SEL$5DA710D3" "D"@"SEL$1" "E"@"SEL$2")
      USE_MERGE(@"SEL$5DA710D3" "E"@"SEL$2")
      END_OUTLINE_DATA
  */
Predicate Information (identified by operation id):
---------------------------------------------------
   4 - access("D"."DEPTNO"="E"."DEPTNO")
       filter("D"."DEPTNO"="E"."DEPTNO")
Column Projection Information (identified by operation id):
-----------------------------------------------------------
   1 - "D"."DEPTNO"[NUMBER,22], "D"."LOC"[VARCHAR2,13], "D"."DNAME"[VARCHAR2,14]
   2 - "D"."DEPTNO"[NUMBER,22], "D"."DNAME"[VARCHAR2,14], "D"."LOC"[VARCHAR2,13]
   3 - "D".ROWID[ROWID,10], "D"."DEPTNO"[NUMBER,22]
   4 - (#keys=1) "E"."DEPTNO"[NUMBER,22]
   5 - "E"."DEPTNO"[NUMBER,22]
SQL> alter session set STATISTICS_LEVEL = TYPICAL;

其中,部分参数说明如下。

·Operation:表示访问路径或连接方式。

·Name:表示SQL语句中涉及的对象。

·Starts:表示执行次数。

·E-Rows、E-Bytes、E-Time:表示CBO(Cost-Based Optimization,基于成本的优化)基于现有统计信息评估出来的值。

·Cost(%CPU):表示成本。成本=I/O成本+CPU成本。

·A-Rows、A-Time:表示实际行数、实际时间,为累计值。

·Buffers:表示逻辑读次数,为累计值。

·OMem、1Mem、Used-Mem:表示PGA(Program Global Area,服务进程私有内存区域)的使用情况,仅适用于内存密集型操作,例如,哈希连接、排序或某些位图运算符等,我们会在后面章节表的连接方式中具体讲解。

除了以上三种执行计划的查看方法之外,还有10046或SQL跟踪、V$SQL_PLAN、PL/SQL等,有条件的还可以使用Oracle的EM和GridControl图形化查看工具,笔者更为推荐的是DBMS_XPLAN方法,其是SQL真实执行中较为准确的执行计划。

4.1.2 读懂执行计划

执行计划贯穿于Oracle SQL调优的始终,看懂执行计划是SQL优化的第一步,很多书上甚至网上对执行计划的解读都是最右最上最先执行的原则,如图4-3所示。

按网上的一般说法,第10步(图中标箭头处)是SQL入口,首先是访问表CUSTOMERS,这样到底对不对?下面我们使用10046进行验证,为了排除缓存的影响,首先清空buffer cache(缓冲区缓存)。验证代码如下:


SQL> alter system flush buffer_cache;
SQL> oradebug setmypid
SQL> oradebug EVENT 10046 TRACE NAME CONTEXT FOREVER, LEVEL 12
SQL> oradebug TRACEFILE_NAME
/u01/app/diag/rdbms/jason/jason1/trace/jason1_ora_32569.trc
SQL> SELECT c.cust_city,
  2         t.calendar_quarter_desc,
  3         SUM(s.amount_sold) sales_amount
  4    FROM SH.sales s, SH.times t, SH.customers c, SH.channels ch
  5   WHERE s.time_id = t.time_id
  6     AND s.cust_id = c.cust_id
  7     AND s.channel_id = ch.channel_id
  8     AND c.cust_state_province = 'Galway'
  9     AND ch.channel_desc = 'Internet'
 10   GROUP BY c.cust_city, t.calendar_quarter_desc
 11   order by 3 desc;
…………
SQL> oradebug EVENT 10046 trace name context off

图4-3 SQL执行计划信息示例

10046部分跟踪内容如下:


nam='Disk file operations I/O' obj#=88066
nam='db file sequential read' obj#=88066
nam='gc cr grant 2-way' obj#=88067
nam='db file sequential read' obj#=88067
nam='db file sequential read' obj#=88063 
nam='gc cr multi block request' obj#=88063 
nam='db file scattered read' obj#=88063 

SQL> select object_id,object_name,STATUS from dba_objects where object_id in 
    (88066,88067,88063);
 OBJECT_ID OBJECT_NAME          STATUS
---------- -------------------- -------
     88063 CUSTOMERS            VALID
     88066 TIMES                VALID
     88067 CHANNELS             VALID

通过上述验证我们可以得知,执行入口是88066 TIMES表,所以说最右最上最先执行的说法是不严谨的。

下面为大家介绍一种方法:树形图解法,即阅读执行计划的时候,自顶而下画树形结构。下面以图4-4所示的执行计划为例进行讲解。

图4-4 执行计划示例

执行计划示例的树形结构如图4-5所示。

图4-5 执行计划示例的树形结构

0为树的根节点;1缩进一格,为0的儿子;2、4缩进相同为兄弟节点,同为1的儿子,2在上为兄,是1的左子树,4在下为弟,是1的右子树;3相对于2缩进一格,为2的儿子,同理,5为4的儿子。

判断树形结构各节点关系的方法如下。

1)自顶而下。

2)最接近的上方,并且前进一格为父子节点。

3)同一父亲、相同缩进,为兄弟节点。

图4-6是对执行计划示例绘制树形结构的分步图解。

图4-6 分布图解

画好树形结构之后,SQL语句就可以借助树形结构的后续遍历法按顺序执行了。如图4-7所示,遍历顺序归纳如下。

1)先遍历左子树。

2)再遍历右子树。

3)左节点先于右节点执行。

4)子节点先于父节点执行。

5)对于相同缩进、上下同父的兄弟节点,兄先执行。

6)最后访问根节点。

在图4-7中,先访问左子树,其中3为2的儿子,先执行3,再执行2,然后访问右子树,5为4的儿子,先执行5再执行4,左右子树执行完再访问根节点1和0,最终的执行顺序为:3-2-5-4-1-0。

图4-8展示的是本节开头处(见图4-3)示例的执行计划的树形结构。

图4-7 遍历顺序

图4-8 图4-3所示示例执行计划的树形结构

在图4-8中,最终的执行顺序为5-4-8-10-9-7-12-11-6-3-2-1-0。

至此,各位读者应该都掌握了执行计划的树形构图法和后序遍历访问法,只要大家多加练习,掌握规则,不画图也能直接看懂执行计划。

树形结构法是笔者从多年工作经验中总结出来的方法。除此之外,大家还可以借助V$SQL_PLAN视图查看执行计划,如图4-9所示。

图4-9 V$SQL_PLAN视图查看执行计划

图4-9中的部分参数说明如下。

·POSITION:缩进相同表示有同一个父节点,子节点的执行顺序为先兄节点后弟节点。

·PARENT_ID:表示父节点ID,子节点的输出结果传递给父节点后再执行下一步。

·DEPTH:表示向右缩进的层数。

查看执行计划最主要的两个部分:Plan hash value和Predicate Information,前者我们在上半部分已经介绍过了,Predicate Information(谓词信息)很关键,一般是指对象的连接和访问方式。细心的读者可能已经发现了执行计划ID列部分带有“*”号,表示在该步骤上发生了谓词过滤。比如,下方的2和4,其中,DEPT表通过DNAME='ACCOUNTING'过滤出了符合条件的行,再与EMP的DEPTNO字段进行关联。示例代码如下:


SQL> select e.empno, e.ename, e.job, d.dname
  from scott.emp e, scott.dept d
 where e.deptno = d.deptno
   and d.dname = 'ACCOUNTING';
------------------------------------------------------------------------------------------
| Id  | Operation                     | Name    | Rows  | Bytes | Cost (%CPU)| Time      |
------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT              |         |     5 |   170 |     6  (17)| 00:00:01  |
|   1 |  MERGE JOIN                   |         |     5 |   170 |     6  (17)| 00:00:01  |
|*  2 |   TABLE ACCESS BY INDEX ROWID | DEPT    |     1 |    13 |     2   (0)| 00:00:01  |
|   3 |    INDEX FULL SCAN            | PK_DEPT |     4 |       |     1   (0)| 00:00:01  |
|*  4 |   SORT JOIN                   |         |    14 |   294 |     4  (25)| 00:00:01  |
|   5 |    TABLE ACCESS FULL          | EMP     |    14 |   294 |     3   (0)| 00:00:01  |
------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   2 - filter("D"."DNAME"='ACCOUNTING')
   4 - access("E"."DEPTNO"="D"."DEPTNO")
       filter("E"."DEPTNO"="D"."DEPTNO")

下面笔者为大家介绍一些工作中比较常用的执行计划查看语句。

·查看当前SQL在内存中的最后一次执行计划,命令如下:


SQL> SELECT RPAD('Inst: ' || v.inst_id, 9) || ' ' ||
       RPAD('Child: ' || v.child_number, 11) inst_child,
       t.plan_table_output
  FROM gv$sql v,
       TABLE(DBMS_XPLAN.DISPLAY('gv$sql_plan_statistics_all',
                                NULL,
                                'ADVANCED ALLSTATS LAST -Projection -Outline -Note',
                                'inst_id = ' || v.inst_id ||
                                ' AND sql_id = ''' || v.sql_id ||
                                ''' AND child_number = ' || v.child_number)) t
 WHERE v.sql_id = '&SQL_ID'
   AND v.loaded_versions > 0;

·查看当前SQL在内存中的所有执行计划,命令如下:


SQL> SELECT RPAD('Inst: ' || v.inst_id, 9) || ' ' ||
       RPAD('Child: ' || v.child_number, 11) inst_child,
       t.plan_table_output
  FROM gv$sql v,
       TABLE(DBMS_XPLAN.DISPLAY('gv$sql_plan_statistics_all',
                                NULL,
                                'ADVANCED ALLSTATS -Projection -Outline -Note',
                                'inst_id = ' || v.inst_id ||
                                ' AND sql_id = ''' || v.sql_id ||
                                ''' AND child_number = ' || v.child_number)) t
 WHERE v.sql_id = '&SQL'
   AND v.loaded_versions > 0
   AND v.executions > 1;

·查看指定SQL的历史执行计划,包括记录在快照点中的执行计划,命令如下:


SQL> SELECT t.plan_table_output
  FROM (SELECT DISTINCT sql_id, plan_hash_value, dbid
          FROM dba_hist_sql_plan
         WHERE sql_id = '&SQL') v,
       TABLE(DBMS_XPLAN.DISPLAY_AWR(v.sql_id,
                                    v.plan_hash_value,
                                    null,
                                    'ADVANCED ALLSTATS')) t;