读懂Oracle执行计划(一)

为什么要有执行计划

因为Oracle的SQL语言是一种“傻瓜”式的语言,需要交给优化器去判断怎么访问更快。

Oracle的执行计划准吗(搜集统计信息)

比如小表驱动大表效率比大表驱动小表更快。那Oracle是怎么知道哪个是小表哪个是大表呢,Oracle的执行计划准吗?

这个就涉及到Oracle收集统计信息。比如Oracle 11g1默认启动了统计信息收集的任务(也可以关闭自动统计新收集任务,选择手工收集的方式,但是一般不建议这样操作),就是周一到周五晚上十点统计相应信息,周六周日早上六点搜集统计信息,比如搜集表的数据大小、索引、列的相关信息(比如列上面的数据有没有倾斜,意思就是比如表中的某一列的数据大部分都是某个值A,只有很少的数据是另一种值B,我们就说数据是倾斜的,那么在查询的时候,如果查询的是A就会全表扫描,是B就会走索引)等。

但是还有一种情况,Oracle 11g就是周一到周五晚上十点统计相应信息,那假如在早上八点新建了一张表,那么这张表的数据不久无法统计了吗,Oracle还有一种动态采样机制,即如果没有搜集到统计信息,那么Oracle会采用动态采样的方式去搜集统计信息。这时候搜集的统计信息不会像搜集统计信息那样放到数据字典中,而是会放到内存中。

Oracle也会在建索引的过程中动态的搜集统计信息。

可以执行这段SQL查看Oracle搜集统计信息的日志:

  select window_name, 
         window_next_time, 
         autotask_status, 
         optimizer_stats
    from DBA_AUTOTASK_WINDOW_CLIENTS;

获取执行计划的方法

6种方法:
    1. explain plan for获取(即PL/SQL中的F5); 
    2. set autotrace on (跟踪性能统计);    
    3. statistics_level=all(获取表访问次数);
    4. 通过dbms_xplan.display_cursor输入sql_id参数直接获取
    5. 10046 trace跟踪
    6. awrsqrpt.sql

各自的适用场景:

    1.如果某SQL执行非常长时间才会出结果,甚至慢到返回不了结果,这时候看执行计划就只能用方法1,或者方法4调用现成的;
    2.跟踪某条SQL最简单的方法是方法1,其次就是方法2;
    3.如果想观察到某条SQL有多条执行计划的情况,只能用方法4和方法6;
    4.如果SQL中含有多函数,函数中套有SQL等多层递归调用,想准确分析,只能使用方法5;
    5.要想确保看到真实的执行计划,不能用方法1和方法2;
    6.要想获取表被访问的次数,只能使用方法3;

读懂执行计划

环境准备:

DROP TABLE t1 CASCADE CONSTRAINTS PURGE; 
DROP TABLE t2 CASCADE CONSTRAINTS PURGE; 
CREATE TABLE t1 (
     id NUMBER NOT NULL,
     n NUMBER,
     contents VARCHAR2(4000)
   )
   ; 
CREATE TABLE t2 (
     id NUMBER NOT NULL,
     t1_id NUMBER NOT NULL,
     n NUMBER,
     contents VARCHAR2(4000)
   )
   ; 
execute dbms_random.seed(0); 
INSERT INTO t1
     SELECT  rownum,  rownum, dbms_random.string('a', 50)
       FROM dual
     CONNECT BY level <= 1000
      ORDER BY dbms_random.random; 
INSERT INTO t2 SELECT rownum, rownum, rownum, dbms_random.string('b', 50) FROM dual CONNECT BY level <= 100000
    ORDER BY dbms_random.random; 
COMMIT; 
CREATE INDEX t1_n ON t1 (n);
CREATE INDEX t2_t1_id ON t2(t1_id);

接下来使用多种方式查看这条SQL的执行计划:

SELECT  *
FROM t1, t2
WHERE t1.id = t2.t1_id
AND t1.n in(18,19);

第一种方法(explain plan for 的方式。类似PLSQL DEVELOPE里的F5)

这种方法的详细介绍可参看:https://blog.csdn.net/Dongguabai/article/details/84309062

 步骤1:explain plan for "你的SQL"
 步骤2:select * from table(dbms_xplan.display()); 

执行SQL(有时候会需要先exit一下):

set linesize 1000
set pagesize 2000
explain plan for
SELECT  *
FROM t1, t2
WHERE t1.id = t2.t1_id
AND t1.n in(18,19);
select * from table(dbms_xplan.display());

也可以在PL/SQL里面F5查看:

基本上只要一提到执行计划,很多人都会想到这个方法,但是要注意的是使用这种方式查看到的执行计划是预估的值,也就是说数据是假的。

优点:  1.无需真正执行,快捷方便

缺陷:  1.没有输出运行时的相关统计信息(产生多少逻辑读,多少次递归调用,多少次物理读的情况);
        2.无法判断是处理了多少行;
        3.无法判断表被访问了多少次。
        
确实啊,这毕竟都没有真正执行又如何得知真实运行产生的统计信息。

第二种方式(set autotrace on 方式)

  步骤1:set autotrace on 
  步骤2:在此处执行你的SQL即可,后续自然会有结果输出

有如下命令

set autotrace on   (得到执行计划,输出运行结果)
set autotrace traceonly (得到执行计划,不输出运行结果)
set autotrace traceonly explain (得到执行计划,不输出运行结果和统计信息部分,仅展现执行计划部分)
set autotrace traceonl statistics(不输出运行结果和执行计划部分,仅展现统计信息部分)

Autotrace执行计划的各列的涵义  
  
序号 列名   解释   
1 ID_PLUS_EXP 每一步骤的行号   
2 PARENT_ID_PLUS_EXP 每一步的Parent的级别号   
3 PLAN_PLUS_EXP 实际的每步   
4 OBJECT_NODE_PLUS_EXP   Dblink或并行查询时才会用到   
  
四、AUTOTRACE Statistics常用列解释  
  
序号 统计列 解释   
1 db block gets 从buffer cache中读取的block的数量   
2 consistent gets 从buffer cache中读取的undo数据的block的数量   
3 physical reads 从磁盘读取的block的数量   
4 redo size DML生成的redo的大小   
5 sorts (memory) 在内存执行的排序量   
7 sorts (disk) 在磁盘上执行的排序量

执行SQL:

set autotrace on 
SELECT  *
FROM t1, t2
WHERE t1.id = t2.t1_id
AND t1.n in(18,19);

可以看到这种方式相比第一种方法能够获取更多的信息。

优点:1.可以输出运行时的相关统计信息(产生多少逻辑读,多少次递归调用,多少次物理读的情况);
           2.虽然必须要等语句执行完毕后才可以输出执行计划,但是可以有traceonly开关来控制返回结果不打屏输出。

 缺陷:1.必须要等到语句真正执行完毕后,才可以出结果;
           2.无法看到表被访问了多少次。  

第三种方式(statistics level=all的方式)

  步骤1:alter session set statistics_level=all ;
  步骤2:在此处执行你的SQL
  步骤3:select * from table(dbms_xplan.display_cursor(null,null,'allstats last'));

另注:
  1. 如果你用 /*+ gather_plan_statistics */的方法,可以省略步骤1,直接步骤2,3。
  2. 关键字解读(其中OMem、1Mem和User-Mem在后续的课程中会陆续见到): 

  • Starts为该sql执行的次数。
  • E-Rows为执行计划预计的行数。
  • A-Rows为实际返回的行数。A-Rows跟E-Rows做比较,就可以确定哪一步执行计划出了问题。
  • A-Time为每一步实际执行的时间(HH:MM:SS.FF),根据这一行可以知道该sql耗时在了哪个地方。
  • Buffers为每一步实际执行的逻辑读或一致性读。
  • Reads为物理读。
  • OMem:当前操作完成所有内存工作区(Work Aera)操作所总共使用私有内存(PGA)中工作区的大小,这个数据是由优化器统计数据以及前一次执行的性能数据估算得出的
  • 1Mem:当工作区大小无法满足操作所需的大小时,需要将部分数据写入临时磁盘空间中(如果仅需要写入一次就可以完成 操作,就称一次通过,One-Pass;否则为多次通过,Multi_Pass).该列数据为语句最后一次执行中,单次写磁盘所需要的内存大小,这个由优化器统计数据以及前一次执行的性能数据估算得出的。
  • User-Mem:语句最后一次执行中,当前操作所使用的内存工作区大小,括号里面为(发生磁盘交换的次数,1次即为One-   Pass,大于1次则为Multi_Pass,如果没有使用磁盘,则显示OPTIMAL)
  • OMem、1Mem为执行所需的内存评估值,0Mem为最优执行模式所需内存的评估值,1Mem为one-pass模式所需内存的评估值。
  • 0/1/M 为最优/one-pass/multipass执行的次数。Used-Mem耗的内存

执行这样一段SQL:

set autotrace off          
alter session set statistics_level=all ;
SELECT  *
FROM t1, t2
WHERE t1.id = t2.t1_id
AND t1.n in(18,19);
select * from table(dbms_xplan.display_cursor(null,null,'allstats last'));

优点:1.可以清晰的从STARTS得出表被访问多少(因为表的访问开销是很大的,这是很重要的一点)。
           2.可以清晰的从E-ROWS和A-ROWS中得到预测的行数和真实的行数,从而可以准确判断Oracle评估是否准确(很重要的一点)。
           3.虽然没有专门的输出运行时的相关统计信息,但是执行计划中的BUFFERS就是真实的逻辑读的多少          

缺陷:1.必须要等到语句真正执行完毕后,才可以出结果。
           2.无法控制记录输屏打出,不像autotrace有 traceonly 可以控制不将结果打屏输出。
           3.看不出递归调用的次数,看不出物理读的多少(不过逻辑读才是重点)

第四种方式(知道sql_id后,直接带入的方式,简单)

步骤1: select  * from table(dbms_xplan.display_cursor('&sq_id')); (该方法是从共享池里得到)

可以通过:

select * from v$sql

查看sql_id:

执行结果:

注:
  1. 还有一个方法,select  * from table(dbms_xplan.display_awr('&sq_id'));(这是awr性能视图里获取到的)
  2. 如果有多执行计划,可以用类似方法查出(0、1、2...)
    select * from table(dbms_xplan.display_cursor('cyzznbykb509s',0));
    select * from table(dbms_xplan.display_cursor('cyzznbykb509s',1));

优点:1.知道sql_id立即可得到执行计划,和explain plan for 一样无需执行;
        2.可以得到真实的执行计划。(停,等等,啥真实的,刚才这几个套路中,还有假的执行计划的吗?)

缺陷:  1.没有输出运行时的相关统计信息(产生多少逻辑读,多少次递归调用,多少次物理读的情况);
        2.无法判断是处理了多少行;  
        3.无法判断表被访问了多少次。

第五种方式(10046TRACE)

  步骤1:alter session set events '10046 trace name context  forever,level 12'; (开启跟踪)
  步骤2:执行你的语句
  步骤3:alter session set events '10046 trace name context off';   (关闭跟踪)
  步骤4:找到跟踪后产生的文件
  步骤5:tkprof  trc文件  目标文件(没有目标文件会弹出打开窗口)  sys=no sort=prsela,exeela,fchela  (格式化命令)

执行:

set autotace off
alter session set statistics_level=typical;     
alter session set events '10046 trace name context  forever,level 12';
SELECT  *
FROM t1, t2
WHERE t1.id = t2.t1_id
AND t1.n in(18,19);   
alter session set events '10046 trace name context off';   

步骤四: 

select d.value
|| '/'
|| LOWER (RTRIM(i.INSTANCE, CHR(0)))
|| '_ora_'
|| p.spid
|| '.trc' trace_file_name
from (select p.spid
      from v$mystat m,v$session s, v$process p
      where  m.statistic#=1 and s.sid=m.sid and p.addr=s.paddr) p,
      (select t.INSTANCE
       FROM v$thread t,v$parameter v
       WHERE v.name='thread'
       AND(v.VALUE=0 OR t.thread#=to_number(v.value))) i,
       (select value
       from v$parameter
       where name='user_dump_dest') d;

退出,进入cmd: 

tkprof C:\oraclexe\app\oracle\diag\rdbms\xe\xe\trace/xe_ora_16272.trc    d:\10046.txt  sys=no sort=prsela,exeela,fchela 

进入cmd执行:

查看文件:

Trace file C:\ORACLEXE\APP\ORACLE\diag\rdbms\xe\xe\trace\xe_ora_16272.trc
Oracle Database 11g Express Edition Release 11.2.0.2.0 - 64bit Production
Windows NT Version V6.2  
CPU                 : 4 - type 8664, 4 Physical Cores
Process Affinity    : 0x0x0000000000000000
Memory (Avail/Total): Ph:3114M/8067M, Ph+PgF:2504M/14738M 
Instance name: xe
Redo thread mounted by this instance: 1
Oracle process number: 29
Windows thread id: 16272, image: ORACLE.EXE (SHAD)


*** 2018-11-20 20:46:53.020
*** SESSION ID:(33.507) 2018-11-20 20:46:53.021
*** CLIENT ID:() 2018-11-20 20:46:53.021
*** SERVICE NAME:(SYS$USERS) 2018-11-20 20:46:53.021
*** MODULE NAME:(SQL*Plus) 2018-11-20 20:46:53.021
*** ACTION NAME:() 2018-11-20 20:46:53.021
 
WAIT #364949696: nam='SQL*Net message to client' ela= 9 driver id=1111838976 #bytes=1 p3=0 obj#=6310 tim=386198577398

*** 2018-11-20 20:47:05.624
WAIT #364949696: nam='SQL*Net message from client' ela= 12602044 driver id=1111838976 #bytes=1 p3=0 obj#=6310 tim=386211195110
CLOSE #364949696:c=0,e=38,dep=0,type=0,tim=386211195581
=====================
PARSING IN CURSOR #364297880 len=582 dep=1 uid=62 oct=3 lid=62 tim=386211199184 hv=3194033346 ad='7ffb17d29ce0' sqlid='gwht6tqz62562'
SELECT /* OPT_DYN_SAMP */ /*+ ALL_ROWS IGNORE_WHERE_CLAUSE NO_PARALLEL(SAMPLESUB) opt_param('parallel_execution_enabled', 'false') NO_PARALLEL_INDEX(SAMPLESUB) NO_SQL_TUNE */ NVL(SUM(C1),:"SYS_B_0"), NVL(SUM(C2),:"SYS_B_1"), COUNT(DISTINCT C3), NVL(SUM(CASE WHEN C3 IS NULL THEN :"SYS_B_2" ELSE :"SYS_B_3" END),:"SYS_B_4") FROM (SELECT /*+ IGNORE_WHERE_CLAUSE NO_PARALLEL("T1") FULL("T1") NO_PARALLEL_INDEX("T1") */ :"SYS_B_5" AS C1, CASE WHEN "T1"."N"=:"SYS_B_6" OR "T1"."N"=:"SYS_B_7" THEN :"SYS_B_8" ELSE :"SYS_B_9" END AS C2, "T1"."ID" AS C3 FROM "BDATAGER"."T1" "T1") SAMPLESUB
END OF STMT
PARSE #364297880:c=0,e=456,p=0,cr=0,cu=0,mis=1,r=0,dep=1,og=1,plh=0,tim=386211199183
BINDS #364297880:
 Bind#0
  oacdty=02 mxl=22(01) mxlc=00 mal=00 scl=00 pre=00
  oacflg=10 fl2=0100 frm=00 csi=00 siz=24 off=0
  kxsbbbfp=17e8f8d0  bln=22  avl=01  flg=09
  value=0
 Bind#1
  oacdty=02 mxl=22(01) mxlc=00 mal=00 scl=00 pre=00
  oacflg=10 fl2=0100 frm=00 csi=00 siz=24 off=0
  kxsbbbfp=2194a158  bln=22  avl=01  flg=09
  value=0
 Bind#2
  oacdty=02 mxl=22(02) mxlc=00 mal=00 scl=00 pre=00
  oacflg=10 fl2=0100 frm=00 csi=00 siz=24 off=0
  kxsbbbfp=1c0bcc58  bln=22  avl=02  flg=09
  value=1
 Bind#3
  oacdty=02 mxl=22(01) mxlc=00 mal=00 scl=00 pre=00
  oacflg=10 fl2=0100 frm=00 csi=00 siz=24 off=0
  kxsbbbfp=20451170  bln=22  avl=01  flg=09
  value=0
 Bind#4
  oacdty=02 mxl=22(01) mxlc=00 mal=00 scl=00 pre=00
  oacflg=10 fl2=0100 frm=00 csi=00 siz=24 off=0
  kxsbbbfp=1c177428  bln=22  avl=01  flg=09
  value=0
 Bind#5
  oacdty=02 mxl=22(02) mxlc=00 mal=00 scl=00 pre=00
  oacflg=10 fl2=0100 frm=00 csi=00 siz=24 off=0
  kxsbbbfp=1e1af338  bln=22  avl=02  flg=09
  value=1
 Bind#6
  oacdty=02 mxl=22(02) mxlc=00 mal=00 scl=00 pre=00
  oacflg=10 fl2=0100 frm=00 csi=00 siz=24 off=0
  kxsbbbfp=1c0b0378  bln=22  avl=02  flg=09
  value=18
 Bind#7
  oacdty=02 mxl=22(02) mxlc=00 mal=00 scl=00 pre=00
  oacflg=10 fl2=0100 frm=00 csi=00 siz=24 off=0
  kxsbbbfp=1c0c1768  bln=22  avl=02  flg=09
  value=19
 Bind#8
  oacdty=02 mxl=22(02) mxlc=00 mal=00 scl=00 pre=00
  oacflg=10 fl2=0100 frm=00 csi=00 siz=24 off=0
  kxsbbbfp=1c1518a0  bln=22  avl=02  flg=09
  value=1
 Bind#9
  oacdty=02 mxl=22(01) mxlc=00 mal=00 scl=00 pre=00
  oacflg=10 fl2=0100 frm=00 csi=00 siz=24 off=0
  kxsbbbfp=1c14ebc8  bln=22  avl=01  flg=09
  value=0
EXEC #364297880:c=15625,e=5380,p=0,cr=0,cu=0,mis=1,r=0,dep=1,og=1,plh=405047221,tim=386211207437
FETCH #364297880:c=0,e=1329,p=0,cr=16,cu=0,mis=0,r=1,dep=1,og=1,plh=405047221,tim=386211208864
STAT #364297880 id=1 cnt=1 pid=0 pos=1 obj=0 op='SORT AGGREGATE (cr=16 pr=0 pw=0 time=1364 us)'
STAT #364297880 id=2 cnt=1000 pid=1 pos=1 obj=0 op='VIEW  VW_DAG_0 (cr=16 pr=0 pw=0 time=1014 us cost=3 size=4264 card=82)'
STAT #364297880 id=3 cnt=1000 pid=2 pos=1 obj=0 op='HASH GROUP BY (cr=16 pr=0 pw=0 time=1011 us cost=3 size=2132 card=82)'
STAT #364297880 id=4 cnt=1000 pid=3 pos=1 obj=39001 op='TABLE ACCESS FULL T1 (cr=16 pr=0 pw=0 time=158 us cost=2 size=2132 card=82)'
CLOSE #364297880:c=0,e=17,dep=1,type=0,tim=386211210403
=====================
PARSING IN CURSOR #364297880 len=544 dep=1 uid=62 oct=3 lid=62 tim=386211212165 hv=2462745574 ad='7ffb17d197d0' sqlid='bfuv67y9cp0z6'
SELECT /* OPT_DYN_SAMP */ /*+ ALL_ROWS IGNORE_WHERE_CLAUSE NO_PARALLEL(SAMPLESUB) opt_param('parallel_execution_enabled', 'false') NO_PARALLEL_INDEX(SAMPLESUB) NO_SQL_TUNE */ NVL(SUM(C1),:"SYS_B_0"), NVL(SUM(C2),:"SYS_B_1"), COUNT(DISTINCT C3), NVL(SUM(CASE WHEN C3 IS NULL THEN :"SYS_B_2" ELSE :"SYS_B_3" END),:"SYS_B_4") FROM (SELECT /*+ NO_PARALLEL("T2") FULL("T2") NO_PARALLEL_INDEX("T2") */ :"SYS_B_5" AS C1, :"SYS_B_6" AS C2, "T2"."T1_ID" AS C3 FROM "BDATAGER"."T2" SAMPLE BLOCK (:"SYS_B_7" , :"SYS_B_8") SEED (:"SYS_B_9") "T2") SAMPLESUB
END OF STMT
PARSE #364297880:c=0,e=1054,p=0,cr=0,cu=0,mis=1,r=0,dep=1,og=1,plh=0,tim=386211212164
BINDS #364297880:
 Bind#0
  oacdty=02 mxl=22(01) mxlc=00 mal=00 scl=00 pre=00
  oacflg=10 fl2=0100 frm=00 csi=00 siz=24 off=0
  kxsbbbfp=1c183328  bln=22  avl=01  flg=09
  value=0
 Bind#1
  oacdty=02 mxl=22(01) mxlc=00 mal=00 scl=00 pre=00
  oacflg=10 fl2=0100 frm=00 csi=00 siz=24 off=0
  kxsbbbfp=1c25d908  bln=22  avl=01  flg=09
  value=0
 Bind#2
  oacdty=02 mxl=22(02) mxlc=00 mal=00 scl=00 pre=00
  oacflg=10 fl2=0100 frm=00 csi=00 siz=24 off=0
  kxsbbbfp=1c17f8a8  bln=22  avl=02  flg=09
  value=1
 Bind#3
  oacdty=02 mxl=22(01) mxlc=00 mal=00 scl=00 pre=00
  oacflg=10 fl2=0100 frm=00 csi=00 siz=24 off=0
  kxsbbbfp=1e18db48  bln=22  avl=01  flg=09
  value=0
 Bind#4
  oacdty=02 mxl=22(01) mxlc=00 mal=00 scl=00 pre=00
  oacflg=10 fl2=0100 frm=00 csi=00 siz=24 off=0
  kxsbbbfp=1c25d700  bln=22  avl=01  flg=09
  value=0
 Bind#5
  oacdty=02 mxl=22(02) mxlc=00 mal=00 scl=00 pre=00
  oacflg=10 fl2=0500 frm=00 csi=00 siz=24 off=0
  kxsbbbfp=1c1c1be8  bln=22  avl=02  flg=09
  value=1
 Bind#6
  oacdty=02 mxl=22(02) mxlc=00 mal=00 scl=00 pre=00
  oacflg=10 fl2=0500 frm=00 csi=00 siz=24 off=0
  kxsbbbfp=1c1710f8  bln=22  avl=02  flg=09
  value=1
 Bind#7
  oacdty=02 mxl=22(03) mxlc=00 mal=00 scl=00 pre=00
  oacflg=10 fl2=0100 frm=00 csi=00 siz=24 off=0
  kxsbbbfp=1c33f9f8  bln=22  avl=03  flg=09
  value=6.3
 Bind#8
  oacdty=02 mxl=22(02) mxlc=00 mal=00 scl=00 pre=00
  oacflg=10 fl2=0100 frm=00 csi=00 siz=24 off=0
  kxsbbbfp=1c28a8f0  bln=22  avl=02  flg=09
  value=1
 Bind#9
  oacdty=02 mxl=22(02) mxlc=00 mal=00 scl=00 pre=00
  oacflg=10 fl2=0100 frm=00 csi=00 siz=24 off=0
  kxsbbbfp=1c1fea68  bln=22  avl=02  flg=09
  value=1
EXEC #364297880:c=0,e=4410,p=0,cr=0,cu=0,mis=1,r=0,dep=1,og=1,plh=1741831035,tim=386211217774
FETCH #364297880:c=15625,e=3785,p=0,cr=78,cu=0,mis=0,r=1,dep=1,og=1,plh=1741831035,tim=386211221639
STAT #364297880 id=1 cnt=1 pid=0 pos=1 obj=0 op='SORT AGGREGATE (cr=78 pr=0 pw=0 time=3814 us)'
STAT #364297880 id=2 cnt=7347 pid=1 pos=1 obj=0 op='VIEW  VW_DAG_0 (cr=78 pr=0 pw=0 time=3917 us cost=3 size=195 card=5)'
STAT #364297880 id=3 cnt=7347 pid=2 pos=1 obj=0 op='HASH GROUP BY (cr=78 pr=0 pw=0 time=3282 us cost=3 size=125 card=5)'
STAT #364297880 id=4 cnt=7347 pid=3 pos=1 obj=39002 op='TABLE ACCESS SAMPLE T2 (cr=78 pr=0 pw=0 time=154 us cost=2 size=125 card=5)'
CLOSE #364297880:c=0,e=6,dep=1,type=0,tim=386211222022
=====================
PARSING IN CURSOR #364949696 len=63 dep=0 uid=62 oct=3 lid=62 tim=386211223339 hv=116898580 ad='7ffb17d5ea68' sqlid='1a914ws3ggfsn'
SELECT  *
FROM t1, t2
WHERE t1.id = t2.t1_id
AND t1.n in(18,19)
END OF STMT
PARSE #364949696:c=31250,e=27620,p=0,cr=98,cu=0,mis=1,r=0,dep=0,og=1,plh=3532430033,tim=386211223338
EXEC #364949696:c=0,e=60,p=0,cr=0,cu=0,mis=0,r=0,dep=0,og=1,plh=3532430033,tim=386211224412
WAIT #364949696: nam='SQL*Net message to client' ela= 4 driver id=1111838976 #bytes=1 p3=0 obj#=6310 tim=386211224541
FETCH #364949696:c=0,e=38,p=0,cr=6,cu=0,mis=0,r=1,dep=0,og=1,plh=3532430033,tim=386211224675
WAIT #364949696: nam='SQL*Net message from client' ela= 335 driver id=1111838976 #bytes=1 p3=0 obj#=6310 tim=386211225520
WAIT #364949696: nam='SQL*Net message to client' ela= 2 driver id=1111838976 #bytes=1 p3=0 obj#=6310 tim=386211225652
FETCH #364949696:c=0,e=116,p=0,cr=8,cu=0,mis=0,r=1,dep=0,og=1,plh=3532430033,tim=386211225728
STAT #364949696 id=1 cnt=2 pid=0 pos=1 obj=0 op='NESTED LOOPS  (cr=14 pr=0 pw=0 time=72 us)'
STAT #364949696 id=2 cnt=2 pid=1 pos=1 obj=0 op='NESTED LOOPS  (cr=12 pr=0 pw=0 time=99 us cost=8 size=8138 card=2)'
STAT #364949696 id=3 cnt=2 pid=2 pos=1 obj=0 op='INLIST ITERATOR  (cr=7 pr=0 pw=0 time=73 us)'
STAT #364949696 id=4 cnt=2 pid=3 pos=1 obj=39001 op='TABLE ACCESS BY INDEX ROWID T1 (cr=7 pr=0 pw=0 time=70 us cost=4 size=4056 card=2)'
STAT #364949696 id=5 cnt=2 pid=4 pos=1 obj=39003 op='INDEX RANGE SCAN T1_N (cr=5 pr=0 pw=0 time=20 us cost=2 size=0 card=4)'
STAT #364949696 id=6 cnt=2 pid=2 pos=2 obj=39004 op='INDEX RANGE SCAN T2_T1_ID (cr=5 pr=0 pw=0 time=22 us cost=1 size=0 card=1)'
STAT #364949696 id=7 cnt=2 pid=1 pos=2 obj=39002 op='TABLE ACCESS BY INDEX ROWID T2 (cr=2 pr=0 pw=0 time=7 us cost=2 size=2041 card=1)'

*** 2018-11-20 20:47:19.273
WAIT #364949696: nam='SQL*Net message from client' ela= 13617051 driver id=1111838976 #bytes=1 p3=0 obj#=6310 tim=386224843380
CLOSE #364949696:c=0,e=44,dep=0,type=0,tim=386224843833
=====================
PARSING IN CURSOR #364949696 len=55 dep=0 uid=62 oct=42 lid=62 tim=386224844060 hv=2217940283 ad='0' sqlid='06nvwn223659v'
alter session set events '10046 trace name context off'
END OF STMT
PARSE #364949696:c=0,e=123,p=0,cr=0,cu=0,mis=0,r=0,dep=0,og=0,plh=0,tim=386224844059
EXEC #364949696:c=0,e=1340,p=0,cr=0,cu=0,mis=0,r=0,dep=0,og=0,plh=0,tim=386224845780

优点:1.可以看出SQL语句对应的等待事件
        2.如果SQL语句中有函数调用,SQL中有SQL,将会都被列出,无处遁形。
        3.可以方便的看出处理的行数,产生的物理逻辑读。
        4.可以方便的看出解析时间和执行时间。
        5.可以跟踪整个程序包

缺陷: 1.步骤繁琐,比较麻烦
        2.无法判断表被访问了多少次。
        3.执行计划中的谓词部分不能清晰的展现出来。

第六种方式(awrsqrpt.sql工具)

 步骤1:@?/rdbms/admin/awrsqrpt.sql
 步骤2:选择你要的断点(begin snap 和end snap)
 步骤3:输入你的sql_id   

直接回车:

分别输入开始和结束的Snap Id:

输入sql_id即可:

最后会生成报表。

具体操作可参看:https://blog.csdn.net/Dongguabai/article/details/84309887

参考资料:

https://blog.csdn.net/bedpotato/article/details/8037131  

https://blog.csdn.net/Dongguabai/article/details/84309062

Oracle 中,执行计划是查询语句在数据库中执行时的详细步骤和访问路径的描述。通过阅读执行计划,可以了解查询语句的执行过程、资源消耗和性能瓶颈,从而进行性能优化。 以下是一些常见的执行计划解读技巧: 1. 执行计划的查看方法:可以使用 EXPLAIN PLAN 命令或者通过 SQL Developer、Toad、SQL*Plus 等工具获取执行计划。例如,在 SQL Developer 中,可以通过点击工具栏上的"执行计划"按钮来查看执行计划。 2. 访问路径:执行计划中的每一行代表一个操作步骤,如全表扫描、索引扫描、连接操作等。了解每个操作的含义和影响,可帮助分析查询的性能瓶颈。 3. 行数和成本估算:执行计划中的行数和成本估算信息是评估查询效率的重要指标。行数估算表示每个操作步骤返回的行数,成本估算表示每个操作步骤的资源消耗(如 CPU 和 I/O)。通过比较实际行数和估算行数,可以判断是否存在数据倾斜或统计信息不准确等问题。 4. 连接顺序:执行计划中的连接操作顺序可能会对查询性能产生影响。了解连接操作的执行顺序,可以优化查询的连接方式、连接条件和连接顺序,以减少资源消耗和提高执行效率。 5. 索引使用情况:执行计划中的索引扫描操作可以指示查询是否使用了合适的索引。检查索引的选择、范围和使用情况,可以优化查询的索引策略和创建缺失的索引。 6. 排序和分组:执行计划中的排序和分组操作可能会影响查询性能。了解排序和分组的方式、顺序和条件,可以优化查询的排序和分组策略,减少排序和分组操作的资源消耗。 7. 特殊操作:执行计划中可能包含一些特殊操作,如视图、子查询、函数等。了解这些特殊操作的执行方式和性能影响,可帮助优化查询的逻辑和语法。 需要注意的是,执行计划只是一个参考,实际性能优化需要结合具体业务场景和数据库配置进行综合考虑。同时,定期更新统计信息、监控数据库性能并进行测试验证也是性能优化的重要手段。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值