oracle查看执行计划
转自http://blog.itpub.net/26736162/viewspace-2136865/
一般来说,有如下几种获取执行计划的方式:
1、AUTOTRACE方式
AUTOTRACE是Oracle自带的客户端工具SQLPlus的一个特性。启用AUTOTRACE后,SQLPlus会自动收集执行过的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 FOR
DBMS_XPLAN.DISPLAY
EXPLAIN PLAN FOR SQL语句;
SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY);
SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY(NULL,NULL,'ADVANCED'));
否
不确定
无
无
无
无
无
无
STATISTICS_LEVEL=ALL
SELECT /+ GATHER_PLAN_STATISTICS/ ...
DBMS_XPLAN.DISPLAY_CURSOR
ALTER SESSION SET STATISTICS_LEVEL=ALL ;
执行SQL
SELECT * 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.sql
SELECT 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报告。
查看执行计划常用方法
1)explain plan命令
2)DBMS_XPLAN包
3)AUTOTRACE开关
4)10046事件
5)10053事件
6)AWR SQL报告
7)PL/SQL工具直接F5
1、 explain plan(相当于PL/SQL的F5)
explain plan for select * from dual;
select * from table(dbms_xplan.display);
注:相关表plan_table$是一个on commit preserve rows的global temporary table。
2、 DBMS_XPLAN包
方法1:配合explain plan使用
explain plan for select * from dual;
select * from table(dbms_xplan.display);
方法2:跟在执行语句后面,‘advanced’比‘all’多显示了“Outline Data”内容
set linesize 800
set pagesize 900
col plan_table_output for a200
select * from table(dbms_xplan.display_cursor(null,null,'advanced'));
方法3:只要目标SQL的执行计划所在的Child Cursor还没有被age out出Shared Pool,就可以使用该方法查看SQL执行计划
select sql_text, sql_id, hash_value, child_number
from v$sql
where sql_text like 'select count(*) from sh.customers%';
select * from table(dbms_xplan.display_cursor('sql_id/hash_value',
child_cursor_number,'advanced'));
方法4:用于查看指定SQL的所有历史执行计划,没有谓词信息
--目标SQL可能有多个Child Cursor,即多个执行计划
select sql_text, sql_id, version_count, executions
from v$sqlarea
where sql_text like 'select count(*) from sh.customers%';
select * from table(dbms_xplan.display_awr('sql_id'));
注:Oracle把执行计划采样数据从V$sql_plan搬到AWR Repository基表wrh$_sql_plan中没有保留谓词信息的记录。
3、 AUTOTRACE开关
可以额外观察到目标SQL执行时所耗费的物理读、逻辑读、产生redo数量以及排序的数量。(statistics)
set outotrace on;
set outotrace off;
set outotrace traceonly;--不现实SQL执行结果
set outotrace traceonly explain;
set outotrace traceonly statistics;
4、 10046事件与tkprof命令
明确显示了目标SQL实际执行计划中每一个执行步骤所消耗的逻辑读、物理读和花费的时间。(在USER_DUMP_DEST目录下生成trace文件。)
--打开
alter session set events '10046 trace name context forever,level 12';
oradebug event 10046 trace name context forever,level 12;
--关闭
alter session set events '10046 trace name context off';
oradebug event 10046 trace name context off;
注:level 12表示trace文件中还包含目标SQL所使用的绑定变量的值以及该session所经历的等待事件。
--操作步骤--
a、SQL> oradebug setmypid;
b、SQL> oradebug event 10046 trace name context forever,level 12;
c、SQL> select count(*) from dual;
d、SQL> oradebug tracefile_name;
/oracle/app/oracle/diag/rdbms/test/test/trace/test_ora_4288.trc
e、SQL> oradebug event 10046 trace name context off;
f、[oracle@test ~]$tkprof /oracle/app/oracle/diag/rdbms/test
/test/trace/test_ora_4288.trc /oracle/test_ora_4288_tkprof.trc
5、 10053事件
6、 AWR SQL报告、Statspack报告
7、 一些现成的脚本(如display_cursor_9i.sql)
8、 PL/SQL工具直接F5
原文地址:http://blog.itpub.net/17203031/viewspace-704626
SQL调优是很多Oracle DBA和开发人员的重要工作。一个高效的SQL改写调优,可以大幅度优化执行计划,提高执行效率,进而增强关键用例模块的可用性和满意度。
进行SQL调优中不可缺少的操作就是获取指定SQL的执行计划。在目前的Oracle版本中,有很多可以使用的执行计划获取方法。本篇就加以总结,供需要的朋友不时之需。
1、方便易用的explain plan
Explain plan命令在Oracle中,可以对后面的SQL语句进行直接的解析,将执行计划保存在一个plan_table的中间表中。之后通过dbms_xplan包的方法进行获取。
ü 确定plan_table的安装
使用explain plan命令的一个前提就是系统中存在plan_table数据表。如果没有的话,需要进行脚本调用安装。
--如果不存在,就生成
SQL> @?/rdbms/admin/catplan.sql
程序包体已创建。
没有错误。
这里注意两个细节:
首先,调用脚本中的?表示ORACLE_HOME目录。如果是使用sqlplus工具,可以直接使用?代指该目录。其他如PL/SQL Developer第三方工具不支持;
其次,如果是Oracle 10g以上的版本,使用脚本名称为catplan.sql。如果是如9i的版本,使用脚本名称为utlxplan.sql。如果在高版本Oracle上使用低版本的plan_table结构,可能在生成执行计划中报错“Plan Table version too old”错误。
ü 使用explain plan for命令生成执行计划并显示
SQL> set linesize 10000;
SQL> set wrap off;
SQL> set pagesize 10000;
SQL> explain plan for select * from scott.emp where empno=7839;
已解释。
之后使用dbms_xplan工具包将生成的执行计划展示出。
SQL> select * from table(dbms_xplan.display);
PLAN_TABLE_OUTPUT
Plan hash value: 2949544139
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
| 0 | SELECT STATEMENT | | 1 | 35 | 1 (0)| 00:00:01 |
| 1 | TABLE ACCESS BY INDEX ROWID| EMP | 1 | 35 | 1 (0)| 00:00:01 |
|* 2 | INDEX UNIQUE SCAN | PK_EMP | 1 | | 0 (0)| 00:00:01 |
Predicate Information (identified by operation id):
2 - access("EMPNO"=7839)
已选择14行。
该语句显示出索引执行计划。
ü 显示详细执行计划信息
上面直接调用,是显示出分析的SQL最简单的执行计划。可以通过设置format参数,显示出关于计划的更详细信息。
SQL> select * from table(dbms_xplan.display(null,null,'advanced'));
PLAN_TABLE_OUTPUT
Plan hash value: 2949544139
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
| 0 | SELECT STATEMENT | | 1 | 35 | 1 (0)| 00:00:01 |
| 1 | TABLE ACCESS BY INDEX ROWID| EMP | 1 | 35 | 1 (0)| 00:00:01 |
|* 2 | INDEX UNIQUE SCAN | PK_EMP | 1 | | 0 (0)| 00:00:01 |
Query Block Name / Object Alias (identified by operation id):
1 - SEL$1 / EMP@SEL$1
2 - SEL$1 / EMP@SEL$1
Outline Data
/*+
BEGIN_OUTLINE_DATA
INDEX(@"SEL$1" "EMP"@"SEL$1" ("EMP"."EMPNO"))
OUTLINE_LEAF(@"SEL$1")
ALL_ROWS
OPTIMIZER_FEATURES_ENABLE('10.2.0.1')
IGNORE_OPTIM_EMBEDDED_HINTS
END_OUTLINE_DATA
*/
Predicate Information (identified by operation id):
2 - access("EMPNO"=7839)
Column Projection Information (identified by operation id):
1 - "EMPNO"[NUMBER,22], "EMP"."ENAME"[VARCHAR2,10],
"EMP"."JOB"[VARCHAR2,9], "EMP"."MGR"[NUMBER,22], "EMP"."HIREDATE"[DATE,7],
"EMP"."SAL"[NUMBER,22], "EMP"."COMM"[NUMBER,22], "EMP"."DEPTNO"[NUMBER,22]
2 - "EMP".ROWID[ROWID,10], "EMPNO"[NUMBER,22]
已选择41行。
添加了format参数,Oracle将更加详细的执行计划信息返回,包括Outline信息、结果集合映射等内容。
ü Explain plan for细节
Explain plan for使用比较方便,特别是可以支持在pl/sql developer等第三方开发工具中使用的特性,比较吸引人。不过,explain plan在使用的时候,要注意一些潜在问题:
首先,explain plan for是单纯对SQL语句进行优化器分析,获取产生到的执行计划。这个过程中,并没有真正执行。所以,生成的执行计划有时候会有bug,而且进行统计的信息情况没有autotrace高;
其次,explain plan for由于只是对执行计划进行估计。所以在有绑定变量的SQL时,生成的执行计划并不准确;
2、 获取“刚刚”的执行计划display_cursor
使用dbms_xplan包,还可以获取刚刚执行过的SQL执行计划信息。
SQL> select * from scott.emp where empno=7900;
EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO
7900 JAMES CLERK 7698 03-12月-81 950 30
SQL> select * from table(dbms_xplan.display_cursor); //获取刚刚的执行计划;
PLAN_TABLE_OUTPUT
SQL_ID 66nkfdw21rc9j, child number 0
select * from scott.emp where empno=7900
Plan hash value: 2949544139
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
| 0 | SELECT STATEMENT | | | | 1 (100)| |
| 1 | TABLE ACCESS BY INDEX ROWID| EMP | 1 | 35 | 1 (0)| 00:00:01 |
|* 2 | INDEX UNIQUE SCAN | PK_EMP | 1 | | 0 (0)| |
Predicate Information (identified by operation id):
2 - access("EMPNO"=7900)
已选择19行。
直接调用display_cursor,不指定sql_id,就可以将刚刚当前会话执行的SQL命令执行计划从library cache中抽取出来。
注意:display_cursor也支持format参数,可以进行详细执行计划信息的抽取。
此外还有一点,就是这种方法获取刚刚执行过的SQL执行计划,只能在sqlplus或者sqlplusw上使用。如果是pl/sql developer等第三方工具,可能不适用。
(注意:在pl/sql developer下使用存在问题)
SQL> select * from table(dbms_xplan.display_cursor);
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_p
8 rows selected
3、autotrace工具使用
本人以为autotrace工具是获取执行计划信息较为完整的工具。优势在于使用该工具可以获取到执行SQL过程中的读写、调用递归和排序分组消耗。
在之前的Blog中,笔者已经撰写过一篇关于autotrace较为详细的文章,有兴趣的读者可以参考:《Autotrace工具使用——小工具,大用场》(http://space.itpub.net/17203031/viewspace-686535)。
在下篇中,我们会介绍直接从shared_pool中抽取执行计划,和从AWR报告库中抽取。最后介绍使用10046事件跟踪执行计划。
上篇中,我们介绍了几种获取执行计划的方法。本篇我们继续探讨其他获取到执行计划详细信息的方法。
4、从shared_pool中直接抽取执行计划
我们执行过的SQL,在Oracle中会将执行计划缓存一段时间,就在shared_pool的library cache中。这是真实使用的执行计划,我们可以使用手段加以抽取展现。
在shared_pool中,执行计划主要是以shared cursor方式进行保存,也就是父子游标方式。一个父游标parent cursor联动若干child cursor,每个child cursor对应一个单独的执行计划。
SQL> select /*+ exp_demo / from scott.emp where empno=7323;
未选定行
从v$sql和v$sqlarea中获取到对应的计划。
//从v$sqlarea中获取到父游标;
SQL> select substr(sql_text,1,20), sql_id, address, version_count,executions from v$sqlarea where sql_text like 'select /*+ exp_demo /%';
SUBSTR(SQL_TEXT,1,20) SQL_ID ADDRESS VERSION_COUNT EXECUTIONS
select /*+ exp_demo a78616x8uja32 2254266C 1 1
//从v$sql中获取到子游标;
SQL> select sql_id, child_number, executions from v$sql where sql_id='a78616x8uja32';
SQL_ID CHILD_NUMBER EXECUTIONS
a78616x8uja32 0 1
获取到sql_id和child_number之后,就可以使用dbms_xplay.display_cursor方法进行抽取。
SQL> select * from table(dbms_xplan.display_cursor('a78616x8uja32',0));
PLAN_TABLE_OUTPUT
SQL_ID a78616x8uja32, child number 0
select /*+ exp_demo / from scott.emp where empno=7323
Plan hash value: 2949544139
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
| 0 | SELECT STATEMENT | | | | 1 (100)| |
| 1 | TABLE ACCESS BY INDEX ROWID| EMP | 1 | 35 | 1 (0)| 00:00:01 |
|* 2 | INDEX UNIQUE SCAN | PK_EMP | 1 | | 0 (0)| |
Predicate Information (identified by operation id):
2 - access("EMPNO"=7323)
已选择19行。
这种方式获取到的执行计划是最准确的执行计划。同样display_cursor也是支持format参数。当使用绑定变量时,还可以抽取出bind peeking的变量取值。
5、从AWR报告库中获取执行计划
直接从shared_pool中获取执行计划,虽然是最准确的但存在实效的问题。如果执行一段时间之后,执行计划shared cursor会由于LRU算法被剔除shared_pool。或者因为环境变化,让执行计划重新生成。所以,很多时候,我们需要更多时间进行SQL分析。
这时候我们就需要AWR(Automatic Workload Repository)的镜像snapshot功能。每个固定时间,Oracle AWR会将系统状况已快照的方式保存下来。这个过程中,也就会将这些shared pool执行计划保存下来。
我们通常使用AWR报告时,发现问题SQL的情况。如下:
我们发现sql_id=’ 4x74bc7r4npq4’的SQL存在执行时间长的问题。此时,该SQL可能已经被置换出SGA,所以可以使用dbms_xplan的display_awr方法抽取AWR存储获取执行计划。
SQL> select * from table(dbms_xplan.display_awr('4x74bc7r4npq4',format => 'advanced'));
PLAN_TABLE_OUTPUT
SQL_ID 4x74bc7r4npq4
select ticket0_.SEQ_NUMBER as SEQ1_324_, ticket0_.VERSION as
VERSION324_, ticket0_.CREATE_DATE as CREATE3_324_, ticket0_.CREATE_USER
(篇幅原因,有删节……)
ticket0_.WEB_SALE_I as WEB121_324_ from BSD_TICKET ticket0_ where
TDNR=:1 and TACN=:2
Plan hash value: 3282229029
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
| 0 | SELECT STATEMENT | | | | 11382 (100)| |
| 1 | TABLE ACCESS FULL| BSD_TICKET | 1 | 582 | 11382 (1)| 00:02:17 |
Query Block Name / Object Alias (identified by operation id):
1 - SEL$1 / TICKET0_@SEL$1
Outline Data
/*+
BEGIN_OUTLINE_DATA
IGNORE_OPTIM_EMBEDDED_HINTS
OPTIMIZER_FEATURES_ENABLE('11.2.0.1')
DB_VERSION('11.2.0.1')
ALL_ROWS
OUTLINE_LEAF(@"SEL$1")
FULL(@"SEL$1" "TICKET0_"@"SEL$1")
END_OUTLINE_DATA
*/
Peeked Binds (identified by position): //绑定变量时用的bind peeking值;
1 - :1 (VARCHAR2(30), CSID=873): '1661663695'
2 - :2 (VARCHAR2(30), CSID=873): '618'
97 rows selected
6、使用10046事件跟踪
传统获取执行计划的方法,是使用10046跟踪事件。通过开启事件跟踪,生成跟踪trace文件。最后通过分析跟踪文件,定位到真实的执行计划。分别按照如下步骤完成:
ü 开启10046跟踪事件,执行诊断SQL
SQL> alter session set events='10046 trace name context forever, level 12';
Session altered
SQL> select * from scott.emp where empno=7323;
EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO
ü 定位跟踪文件
由于使用的是Oracle 10g,笔者使用自定义的函数进行获取。
SQL> select f_get_trace_name from dual;
F_GET_TRACE_NAME
C:\TOOL\ORACLE\ORACLE\PRODUCT\10.2.0\ADMIN\OTS\UDUMP\ots_ora_3388.trc
说明:如果是在Oracle 11g,可以检索视图v$diag_info来获取当前会话的诊断文件名称。
ü 使用tkprof工具进行跟踪文件处理
由于.trc文件大都是粗格式文档,不宜于阅读。所以可以使用tkprof工具对跟踪文件进行处理。
D:>tkprof ots_ora_3388.trc result.txt
TKPROF: Release 10.2.0.1.0 - Production on 星期三 8月 10 10:04:34 2011
Copyright (c) 1982, 2005, Oracle. All rights reserved.
从处理结果文件result.txt中,我们可以找到对应SQL的执行计划信息。
select *
from
scott.emp where empno=7323
call count cpu elapsed disk query current rows
Parse 1 0.03 0.02 0 0 0 0
Execute 1 0.00 0.00 0 0 0 0
Fetch 1 0.00 0.00 0 1 0 0
total 3 0.03 0.02 0 1 0 0
Misses in library cache during parse: 1
Optimizer mode: ALL_ROWS
Parsing user id: SYS
Rows Row Source Operation
0 TABLE ACCESS BY INDEX ROWID EMP (cr=1 pr=0 pw=0 time=45 us)
0 INDEX UNIQUE SCAN PK_EMP (cr=1 pr=0 pw=0 time=30 us)(object id 51152)
Elapsed times include waiting on following events:
Event waited on Times Max. Wait Total Waited
---------------------------------------- Waited ---------- ------------
SQL*Net message to client 2 0.00 0.00
SQL*Net message from client 2 0.03 0.03
该种方法比较复杂,但是获取到的信息很精确。同时,也可以获取到关于SQL处理三阶段(Parse、Execute和Fetch)的相应处理内容。
7、结论
SQL执行计划是我们研究Oracle、研究Oracle优化器的一个重要手段工具。本篇系列关注如何获取SQL的执行计划,列举出六种详细的手段和方法。不同方法均有其优缺点和适应环境,选择正确的方法才可以起到最好的效果。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· [翻译] 为什么 Tracebit 用 C# 开发
· Deepseek官网太卡,教你白嫖阿里云的Deepseek-R1满血版
· 2分钟学会 DeepSeek API,竟然比官方更好用!
· .NET 使用 DeepSeek R1 开发智能 AI 客户端
· 刚刚!百度搜索“换脑”引爆AI圈,正式接入DeepSeek R1满血版