(2)解读执行计划

时间:2021-10-25 04:34:37

1、内部函数与操作

访问条件可以帮助操作从物理对象上定位到符合条件的数据,然后再读取数据;过滤条件是操作已经从物理存储上读取到了数据,然后将不符合条件的数据过滤掉。

实际上,在Oracle 内部,执行计划的每一个数据源(Row Source)操作都与一个内部函数(qer<*>)相对应,而操作对象、谓词条件都是这些函数的参数。这些函数之间可以相互调用,也正是这些函数的调用关系,映射成为执行计划的树状关系。换句话说,一个执行计划告诉Oracle 的内部引擎如何调用这些函数,以及传给函数的参数值。从这个意义上来说,我们可以将一个执行计划视为一段伪代码或者一段解释型语言的代码,SQL 执行引擎则负责解释和执行该段代码。

2、执行计划各个列的含义

Rows/E-Rows :优化器估算出当前操作返回给上一级操作的数据记录数,如果计划中同时输出收集到实际记录数,则会显示为E-Rows 以和实际记录数(A-Rows)区别;
Bytes/E-Bytes:优化器估算出当前操作返回给上一级操作的数据的字节数,如果计划中同时输出收集到实际字节数,则会显示为E-Bytes 以和实际字节数(A-Bytes)区别;
Cost(%CPU) :优化器估算出完成当前操作的代价(包含子操作的代价),它是 IO 代价和CPU 代价总和。其中,IO 代价是最基本的代价。而对于CPU 代价,在默认情况下,优化器会将CPU 代价计算在内,并且将CPU 代价根据系统配置由特定的转换公式转换为IO 代价。也可以通过优化器参数_optimizer_cost_model 指定是否在代价模型中包括CPU 代价。括号中数据即为CPU 代价在总代价中的比例;
Time/E-Time :优化器估算出完成当前操作所需要的时间,这个时间是其子操作的累计时间,如果计划中同时输出收集到实际时间,则会显示为E-Time 以和实际时间(A-Time)区别;
Starts :当前操作实际被启动的次数,如果输出格式中指定了 LAST 关键字,则为计划最后一次执行中当前操作实际被启动的次数,否则为所有被启动次数总和;
Rows :当前操作实际返回的记录数,如果输出格式中指定了 LAST 关键字,则为最后一次执行的记录数,否则为所有执行的记录数总和;
TempSpc/E-Temp :优化器估算出完成当前操作(仅部分操作需要临时空间,如SORT、Hash Join)所需要的临时表空间的大小,如果计划中同时输出收集到实际临时空间大小,则会显示为E-Temp 以和实际临时空间(A-Temp)大小区别;
Pstart:分区裁剪(Partition Prunning)后,访问的起始分区,仅在含有分区表访问操作的执行计划中出现;
Pstop:分区裁剪(Partition Prunning)后,访问的结束分区,仅在含有分区表访问操作的执行计划中出现;
Inst:分布式查询中,远程对象所在的数据库实例名;
TQ:并行查询中的表队列(Table Queue);
IN-OUT:并行查询或分布式查询中数据传输方式;
PQ Distrib:并行查询中,并行服务进程之间的数据分发方式;
Time :执行当前操作的实际时间,如果输出格式中指定了 LAST 关键字,则为最后一次执行的时间,否则为所有执行的时间总和;
Buffers :当前操作中发生读内存的次数,如果输出格式中指定了 LAST 关键字,则为最后一次执行的读内存次数,否则为所有执行的读内存次数总和。内存读次数包括一致性读(Consistent Read,CR)和当前模式读(Current Get,CU);
Reads :当前操作中发生读磁盘的次数,如果输出格式中指定了 LAST 关键字,则为最后一次执行的读磁盘次数,否则为所有执行的读磁盘次数总和;
Writes:当前操作中发生写磁盘的次数,如果输出格式中指定了 LAST 关键字,则为最后一次执行的写磁盘次数,否则为所有执行的写磁盘次数总和;
OMem :当前操作完成所有内存工作区(Work Area)操作所总共使用私有内存(PGA)中工作区的大小。需要使用内存工作区的操作为:哈希操作,如哈希分组(Hash Group)、哈希关联(Hash Join)和排序(Sort)操作,它们分别占有工作区中哈希区(Hash Area)和排序区(Sort Area)进行工作,这个数据是由优化器统计数据以及前一次执行的性能数据估算得出的;
1Mem:当工作区大小无法满足操作所需要的大小时,需要将部分数据写入临时磁盘空间中(如果仅需要写入一次就可以完成操作,就称为一次通过,One-Pass ;否则为多次通过,Multi-Pass)。该列数据为语句最后一次执行中,单次写磁盘所需要的内存大小,这个数据是由优化器统计数据以及前一次执行的性能数据估算得出的;
Used-Mem :语句最后一次执行中,当前操作所使用的内存工作区大小,括号里面为(发生磁盘交换的次数,1 次即为One-Pass,大于1 次则为Multi-Pass,如果没有使用磁盘,则显示OPTIMAL);
Used-Tmp :语句最后一次执行中,当前操作所使用的临时段的大小,无法一次在工作区完成操作的数据被临时写入该段;
O/1/M :语句所有的执行总共使用内存或磁盘完成操作的执行次数,分别是 Optimal(优化的,仅适用内存完成操作)/One-Pass/Multi-Pass;
Max-Tmp:语句所有执行中,当前操作所使用的临时段的最大空间。

3、获取执行计划的函数

/***********************************************************
** 用于10G **
***********************************************************/
create or replace procedure sql_explain (stmt varchar2,
format varchar2 default 'ADVANCED',
exponly boolean default true)
------------------------------------------------------------
-- 描述:解析和显示语句执行计划 ---
-- 参数描述 ---
-- stmt:解析或执行的语句 ---
-- format:执行计划输出格式,参加DBMS_XPLAN中描述 ---
-- exponly:是否仅解析 ---
-- TRUE:仅调用EXPLAIN PLAN命令解析语句 ---
-- FALSE:执行语句后从缓存获得执行计划 ---
------------------------------------------------------------
AUTHID CURRENT_USER
as
c number;
r number;
sqlid varchar2(100);
childnum number;
begin
dbms_output.enable(50000);
if exponly THEN
execute immediate 'explain plan for '||stmt;
for xpl_rec in ( select * from table(dbms_xplan.display(null,null,format)) ) loop
dbms_output.put_line(xpl_rec.plan_table_output);
end loop;
else
c := dbms_sql.open_cursor;
dbms_sql.parse(c,stmt,dbms_sql.native);
r := dbms_sql.execute(c);
if r = 0 then
r := dbms_sql.fetch_rows(c);
while r>0 loop
r := dbms_sql.fetch_rows(c);
end loop;
end if;
select distinct s.sql_id, s.child_number into sqlid, childnum from v$sql_plan s, v$sql_cursor c where s.address=c.PARENT_HANDLE and c.curno=c and rownum<=1;
dbms_sql.close_cursor(c);
for xpl_rec in ( select * from table(dbms_xplan.display_cursor(sqlid,childnum,format)) ) loop
dbms_output.put_line(xpl_rec.plan_table_output);
end loop;
end if;
rollback;
end;
/

grant execute on sql_explain to public;
create or replace public synonym sql_explain for sys.sql_explain;
/************************************************************* 用于11G                                                *************************************************************/create or replace procedure sql_explain (stmt varchar2,                                          format varchar2 default 'ADVANCED',                                          exponly boolean default true)-------------------------------------------------------------- 描述:解析和显示语句执行计划                          ----- 参数描述                                              -----     stmt:解析或执行的语句                            -----     format:执行计划输出格式,参加DBMS_XPLAN中描述    -----     exponly:是否仅解析                               -----         TRUE:仅调用EXPLAIN PLAN命令解析语句          -----         FALSE:执行语句后从缓存获得执行计划           ---------------------------------------------------------------  AUTHID CURRENT_USER as  c number;  r number;  sqlid varchar2(100);  childnum number;begin  dbms_output.enable(50000);  if exponly then    execute immediate 'explain plan for '||stmt;    for xpl_rec in ( select * from table(dbms_xplan.display(null,null,format)) ) loop        dbms_output.put_line(xpl_rec.plan_table_output);    end loop;  else     c := dbms_sql.open_cursor;     dbms_sql.parse(c,stmt,dbms_sql.native);     r := dbms_sql.execute_and_fetch(c);     loop       exit when r <= 0;       r := dbms_sql.fetch_rows(c);     end loop;    select distinct p.sql_id, p.child_number into sqlid, childnum     from v$sql_cursor sc, v$sql_plan p, v$open_cursor c, v$sqlarea q     where p.address=sc.PARENT_HANDLE and p.sql_id=q.sql_id and c.sql_id = q.sql_id and c.sid = SYS_CONTEXT('USERENV','SID') and q.sql_text like substr(stmt,0,30)||chr(37) and rownum<=1;    --select distinct s.sql_id, s.child_number into sqlid, childnum from v$sql_plan s, v$sql_cursor c where s.address=c.PARENT_HANDLE and c.curno=c and rownum<=1;    dbms_sql.close_cursor(c);    for xpl_rec in ( select * from table(dbms_xplan.display_cursor(sqlid,childnum,format)) ) loop        dbms_output.put_line(xpl_rec.plan_table_output);    end loop;  end if;  rollback;end;/grant execute on sql_explain to public;create or replace public synonym sql_explain for sys.sql_explain;
SQL> exec sql_explain('select * from emp where emp_id=1');Plan hash value: 3169874980--------------------------------------------------------------------------------------------| Id  | Operation                   | Name         | Rows  | Bytes | Cost (%CPU)| Time     |--------------------------------------------------------------------------------------------|   0 | SELECT STATEMENT            |              |     1 |    26 |     2   (0)| 00:00:01 ||   1 |  TABLE ACCESS BY INDEX ROWID| EMP          |     1 |    26 |     2   (0)| 00:00:01 ||*  2 |   INDEX UNIQUE SCAN         | SYS_C0021331 |     1 |       |     1   (0)| 00:00:01 |--------------------------------------------------------------------------------------------Query Block Name / Object Alias (identified by operation id):-------------------------------------------------------------1 - SEL$1 / EMP@SEL$12 - SEL$1 / EMP@SEL$1Outline Data-------------/*+BEGIN_OUTLINE_DATAINDEX_RS_ASC(@"SEL$1" "EMP"@"SEL$1" ("EMP"."EMP_ID"))OUTLINE_LEAF(@"SEL$1")ALL_ROWSDB_VERSION('11.2.0.1')OPTIMIZER_FEATURES_ENABLE('11.2.0.1')IGNORE_OPTIM_EMBEDDED_HINTSEND_OUTLINE_DATA*/Predicate Information (identified by operation id):---------------------------------------------------2 - access("EMP_ID"=1)Column Projection Information (identified by operation id):-----------------------------------------------------------1 - "EMP_ID"[NUMBER,22], "EMP"."DEPT_ID"[NUMBER,22]2 - "EMP".ROWID[ROWID,10], "EMP_ID"[NUMBER,22]PL/SQL 过程已成功完成。