- DBA攻坚指南:左手Oracle,右手MySQL
- 叶桦 徐浩 张梦颖 应以峰
- 4579字
- 2021-07-09 20:30:17
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;