Oracle学习笔记读懂执行计划(十八)

获取执行计划的方法:

 

 统计信息:

/*
1. 11g默认启动了统计信息收集的任务,默认运行时间是周一到周五晚上10点和周6,周天的早上6点
2. 你也可以关闭自动统计新收集任务,选择手工收集的方式,但是一般不建议这样操作。
*/


  select window_name, 
         window_next_time, 
         autotask_status, 
         optimizer_stats
    from DBA_AUTOTASK_WINDOW_CLIENTS;

WINDOW_NAME         WINDOW_NEXT_TIME                              AUTOTASK OPTIMIZE
------------------- ------------------------------------------------ -------- -----
MONDAY_WINDOW       13-1月 -14 10.00.00.000000 下午 +08:00        ENABLED  ENABLED
TUESDAY_WINDOW      14-1月 -14 10.00.00.000000 下午 +08:00        ENABLED  ENABLED
WEDNESDAY_WINDOW    15-1月 -14 10.00.00.000000 下午 +08:00        ENABLED  ENABLED
THURSDAY_WINDOW     16-1月 -14 10.00.00.000000 下午 +08:00        ENABLED  ENABLED
FRIDAY_WINDOW       17-1月 -14 10.00.00.000000 下午 +08:00        ENABLED  ENABLED
SATURDAY_WINDOW     11-1月 -14 06.00.00.000000 上午 +08:00        ENABLED  ENABLED
SUNDAY_WINDOW       12-1月 -14 06.00.00.000000 上午 +08:00        ENABLED  ENABLED

select client_name,status from dba_autotask_client
 where client_name='auto optimizer stats collection';

CLIENT_NAME                           STATUS
--------------------------------------------
auto optimizer stats collection      ENABLED

/*
禁用这个任务的sql如下:(其实默认是有auto space advisor段指导、sql tuning advisor自动SQL优化和收集统计信息3个任务。
不过那2个基本没啥用,可以禁掉!):
*/

BEGIN
dbms_auto_task_admin.disable(client_name=>'auto optimizer stats collection',operation=>NULL,window_name=>NULL);
END;

--启用
begin
  dbms_auto_task_admin.enable;
  dbms_auto_task_admin.enable('auto optimizer stats collection', null,null);
end;
begin
  dbms_auto_task_admin.enable('auto optimizer stats collection', null,'WEDNESDAY_WINDOW');
  dbms_auto_task_admin.enable('auto optimizer stats collection', null,'FRIDAY_WINDOW');
  dbms_auto_task_admin.enable('auto optimizer stats collection', null,'SATURDAY_WINDOW');
  dbms_auto_task_admin.enable('auto optimizer stats collection', null,'THURSDAY_WINDOW');
  dbms_auto_task_admin.enable('auto optimizer stats collection', null,'TUESDAY_WINDOW');
  dbms_auto_task_admin.enable('auto optimizer stats collection', null,'SUNDAY_WINDOW');
  dbms_auto_task_admin.enable('auto optimizer stats collection', null,'MONDAY_WINDOW');
end;

 
--配置维护窗口:
--可是使用DBMS_SCHEDULER 包来修改窗口属性。
--1:修改维护窗口
--先禁用维护窗口
BEGIN
dbms_scheduler.disable(
    name  => 'SATURDAY_WINDOW');
--修改维护窗口属性:
dbms_scheduler.set_attribute(
    name      => 'SATURDAY_WINDOW',
    attribute => 'DURATION',
    value     => numtodsinterval(4, 'hour'));
--启用维护窗口
dbms_scheduler.enable(
    name => 'SATURDAY_WINDOW');
END;
/
--对于当前打开的窗口,你需要首先禁用,然后修改再启用,配置立即生效,如果你不通过这三个过程来修改属性,属性是不会生效的,直到下一次窗口打开。

--2:创建新窗口:
BEGIN
dbms_scheduler.create_window(
    window_name     => 'EARLY_MORNING_WINDOW',
    duration        =>  numtodsinterval(1, 'hour'),
    resource_plan   => 'DEFAULT_MAINTENANCE_PLAN',
    repeat_interval => 'FREQ=DAILY;BYHOUR=5;BYMINUTE=0;BYSECOND=0');
dbms_scheduler.add_window_group_member(
    group_name  => 'MAINTENANCE_WINDOW_GROUP',
    window_list => 'EARLY_MORNING_WINDOW');
END;
/
--3:删除窗口:
BEGIN
DBMS_SCHEDULER.REMOVE_WINDOW_GROUP_MEMBER(
    group_name  => 'MAINTENANCE_WINDOW_GROUP',
    window_list => 'EARLY_MORNING_WINDOW');
END;
/
 
--跟踪job运行情况:
--可以通过查询视图DBA_AUTOTASK_HISTORY追踪job运行情况:
select client_name,job_name,job_start_time
     from dba_autotask_job_history where client_name='auto optimizer stats collection'
     order by job_start_time desc;

  动态采样

/*

  结论:
  1. 统计信息默认情况下是每天晚上10点半后收集,如果新建对象还没来得级收集统计信息,就采用动态采样的方式。
  2. 具体在set autotrace 跟踪的执行计划中,可以看到类似:- dynamic sampling used for this statement (level=2)
  3. 除非你用类似/*+dynamic_sampling(t 0) */的HINT关闭这个动态采样。
  4. 在收集过统计信息后,Oracle就不会采用动态采样。
  注:建索引过程中,默认会收集索引相关的统计信息。

*/
 
set autotrace off
set linesize 1000
drop table t_sample purge;
create table t_sample as select * from dba_objects;
create index idx_t_sample_objid on t_sample(object_id);
 select num_rows, blocks, last_analyzed
  from user_tables
 where table_name = 'T_SAMPLE';
 
 NUM_ROWS   BLOCKS   LAST_ANALYZED
----------------------------------

--建索引后,自动收集统计信息。
select index_name, 
       num_rows, 
       leaf_blocks, 
       distinct_keys, 
       last_analyzed
  from user_indexes
 where table_name = 'T_SAMPLE';
 
INDEX_NAME                       NUM_ROWS LEAF_BLOCKS DISTINCT_KEYS LAST_ANALYZED
------------------------------ ---------- ----------- ------------- --------------
IDX_T_SAMPLE_OBJID                  73159         162         73159 11-1月 -14 
 
set autotrace traceonly
set linesize 1000

select  * from t_sample where object_id=20;
执行计划
----------------------------------------------------------
Plan hash value: 1453182238
--------------------------------------------------------------------------------------------------
| Id  | Operation                   | Name               | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |                    |     1 |   207 |     2   (0)| 00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID| T_SAMPLE           |     1 |   207 |     2   (0)| 00:00:01 |
|*  2 |   INDEX RANGE SCAN          | IDX_T_SAMPLE_OBJID |     1 |       |     1   (0)| 00:00:01 |
--------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   2 - access("OBJECT_ID"=20)
Note
-----
   - dynamic sampling used for this statement (level=2)
统计信息
----------------------------------------------------------
          0  recursive calls
          0  db block gets
          4  consistent gets
          0  physical reads
          0  redo size
       1393  bytes sent via SQL*Net to client
        415  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processed
          
--以下方法是要取消动态采样。          
select /*+dynamic_sampling(t 0) */ * from t_sample t where object_id=20;

执行计划
----------------------------------------------------------
Plan hash value: 1453182238
--------------------------------------------------------------------------------------------------
| Id  | Operation                   | Name               | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |                    |   872 |   176K|     6   (0)| 00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID| T_SAMPLE           |   872 |   176K|     6   (0)| 00:00:01 |
|*  2 |   INDEX RANGE SCAN          | IDX_T_SAMPLE_OBJID |   349 |       |     1   (0)| 00:00:01 |
--------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   2 - access("OBJECT_ID"=20)
统计信息
----------------------------------------------------------
          0  recursive calls
          0  db block gets
          4  consistent gets
          0  physical reads
          0  redo size
       1393  bytes sent via SQL*Net to client
        415  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processed




exec dbms_stats.gather_table_stats(ownname => 'LJB',tabname => 'T_SAMPLE',estimate_percent => 10,method_opt=> 'for all indexed columns',cascade=>TRUE) ;  

set autotrace off 
 select num_rows, blocks, last_analyzed
    from user_tables
  where table_name = 'T_SAMPLE';

  NUM_ROWS     BLOCKS LAST_ANALYZED
---------- ---------- --------------
     73630       1068 12-1月 -14

set autotrace traceonly     
select  * from t_sample where object_id=20;
执行计划
----------------------------------------------------------
Plan hash value: 1453182238

--------------------------------------------------------------------------------------------------
| Id  | Operation                   | Name               | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |                    |     1 |    97 |     2   (0)| 00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID| T_SAMPLE           |     1 |    97 |     2   (0)| 00:00:01 |
|*  2 |   INDEX RANGE SCAN          | IDX_T_SAMPLE_OBJID |     1 |       |     1   (0)| 00:00:01 |
--------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   2 - access("OBJECT_ID"=20)


统计信息
----------------------------------------------------------
          0  recursive calls
          0  db block gets
          4  consistent gets
          0  physical reads
          0  redo size
       1393  bytes sent via SQL*Net to client
        415  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processed

--表统计信息收集后,是这样的。          
set autotrace off
 select num_rows, blocks, last_analyzed
  from user_tables
 where table_name = 'T';
 
  NUM_ROWS     BLOCKS LAST_ANALYZED
--------- ---------- --------------
    73118       1068 11-1月 -14

  表访问次数:

/*
  总的结论:
  
  一.获取执行计划的6种方法(详细步骤已经在每个例子的开头注释部分说明了):
    1. explain plan for获取; 
    2. set autotrace on ;    
    3. statistics_level=all;
    4. 通过dbms_xplan.display_cursor输入sql_id参数直接获取
    5. 10046 trace跟踪
    6. awrsqrpt.sql
    
  二.适用场合分析
  
    1.如果某SQL执行非常长时间才会出结果,甚至慢到返回不了结果,这时候看执行计划就只能用方法1;
    2.跟踪某条SQL最简单的方法是方法1,其次就是方法2;
    3.如果想观察到某条SQL有多条执行计划的情况,只能用方法4和方法6;
    4.如果SQL中含有多函数,函数中套有SQL等多层递归调用,想准确分析,只能使用方法5;
    5.要想确保看到真实的执行计划,不能用方法1和方法2;
    6.要想获取表被访问的次数,只能使用方法3;
   
*/
/*

分结论1(表访问次数):

二. 执行计划中"表访问次数” 是关键指标,这只能靠方法3的方式获取。
   隆重推出本次明星:方法3的statistics_level=all;

*/

--环境构造
--研究Nested Loops Join访问次数前准备工作
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);
/*
  下面我们将会用多种方法来查看如下语句的执行计划
SELECT  *
FROM t1, t2
WHERE t1.id = t2.t1_id
AND t1.n in(18,19);

*/

----方法1(explain plan for 的方式。类似PLSQL DEVELOPE里的F5)

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

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());

PLAN_TABLE_OUTPUT
-------------------------------------------------------------------------------------------
Plan hash value: 3532430033
-------------------------------------------------------------------------------------------
| Id  | Operation                      | Name     | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT               |          |     2 |  8138 |     6   (0)| 00:00:01 |
|   1 |  NESTED LOOPS                  |          |       |       |            |          |
|   2 |   NESTED LOOPS                 |          |     2 |  8138 |     6   (0)| 00:00:01 |
|   3 |    INLIST ITERATOR             |          |       |       |            |          |
|   4 |     TABLE ACCESS BY INDEX ROWID| T1       |     2 |  4056 |     2   (0)| 00:00:01 |
|*  5 |      INDEX RANGE SCAN          | T1_N     |     1 |       |     1   (0)| 00:00:01 |
|*  6 |    INDEX RANGE SCAN            | T2_T1_ID |     1 |       |     1   (0)| 00:00:01 |
|   7 |   TABLE ACCESS BY INDEX ROWID  | T2       |     1 |  2041 |     2   (0)| 00:00:01 |
-------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   5 - access("T1"."N"=18 OR "T1"."N"=19)
   6 - access("T1"."ID"="T2"."T1_ID")
Note
-----
   - dynamic sampling used for this statement (level=2)

已选择24行。
/*
优点:  1.无需真正执行,快捷方便

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


----方法2(set autotrace on 方式)
/*
  步骤1:set autotrace on 
  步骤2:在此处执行你的SQL即可,后续自然会有结果输出
  
另,有如下几种方式:
                     set autotrace on                 (得到执行计划,输出运行结果)
                     set autotrace traceonly          (得到执行计划,不输出运行结果)
                     set autotrace traceonly explain  (得到执行计划,不输出运行结果和统计信息部分,仅展现执行计划部分)
                     set autotrace traceonl statistics(不输出运行结果和执行计划部分,仅展现统计信息部分)
*/
set autotrace on 
SELECT  *
FROM t1, t2
WHERE t1.id = t2.t1_id
AND t1.n in(18,19);

执行计划
----------------------------------------------------------
Plan hash value: 3532430033
-------------------------------------------------------------------------------------------
| Id  | Operation                      | Name     | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT               |          |     2 |  8138 |     6   (0)| 00:00:01 |
|   1 |  NESTED LOOPS                  |          |       |       |            |          |
|   2 |   NESTED LOOPS                 |          |     2 |  8138 |     6   (0)| 00:00:01 |
|   3 |    INLIST ITERATOR             |          |       |       |            |          |
|   4 |     TABLE ACCESS BY INDEX ROWID| T1       |     2 |  4056 |     2   (0)| 00:00:01 |
|*  5 |      INDEX RANGE SCAN          | T1_N     |     1 |       |     1   (0)| 00:00:01 |
|*  6 |    INDEX RANGE SCAN            | T2_T1_ID |     1 |       |     1   (0)| 00:00:01 |
|   7 |   TABLE ACCESS BY INDEX ROWID  | T2       |     1 |  2041 |     2   (0)| 00:00:01 |
-------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   5 - access("T1"."N"=18 OR "T1"."N"=19)
   6 - access("T1"."ID"="T2"."T1_ID")
Note
-----
   - dynamic sampling used for this statement (level=2)
统计信息
----------------------------------------------------------
          0  recursive calls
          0  db block gets
         12  consistent gets
          0  physical reads
          0  redo size
       1032  bytes sent via SQL*Net to client
        416  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          2  rows processed

/*
--优点:1.可以输出运行时的相关统计信息(产生多少逻辑读,多少次递归调用,多少次物理读的情况);
        2.虽然必须要等语句执行完毕后才可以输出执行计划,但是可以有traceonly开关来控制返回结果不打屏输出。
                  
--缺陷:1.必须要等到语句真正执行完毕后,才可以出结果;
        2.无法看到表被访问了多少次。        
         
*/         
            
----方法3(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. 关键字解读: 
    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耗的内存
  
*/       
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'));

PLAN_TABLE_OUTPUT
-------------------------------------------------------------------------------------------------------
SQL_ID  1a914ws3ggfsn, child number 0
-------------------------------------
SELECT  * FROM t1, t2 WHERE t1.id = t2.t1_id AND t1.n in(18,19)

Plan hash value: 3532430033
-----------------------------------------------------------------------------------------------------
| Id  | Operation                      | Name     | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
-----------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT               |          |      1 |        |      2 |00:00:00.01 |      12 |
|   1 |  NESTED LOOPS                  |          |      1 |        |      2 |00:00:00.01 |      12 |
|   2 |   NESTED LOOPS                 |          |      1 |      2 |      2 |00:00:00.01 |      10 |
|   3 |    INLIST ITERATOR             |          |      1 |        |      2 |00:00:00.01 |       5 |
|   4 |     TABLE ACCESS BY INDEX ROWID| T1       |      2 |      2 |      2 |00:00:00.01 |       5 |
|*  5 |      INDEX RANGE SCAN          | T1_N     |      2 |      1 |      2 |00:00:00.01 |       3 |
|*  6 |    INDEX RANGE SCAN            | T2_T1_ID |      2 |      1 |      2 |00:00:00.01 |       5 |
|   7 |   TABLE ACCESS BY INDEX ROWID  | T2       |      2 |      1 |      2 |00:00:00.01 |       2 |
-----------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   5 - access(("T1"."N"=18 OR "T1"."N"=19))
   6 - access("T1"."ID"="T2"."T1_ID")
Note
-----
   - dynamic sampling used for this statement (level=2)

已选择29行。
/*
--优点:1.可以清晰的从STARTS得出表被访问多少。
        2.可以清晰的从E-ROWS和A-ROWS中得到预测的行数和真实的行数,从而可以准确判断Oracle评估是否准确。
        3.虽然没有专门的输出运行时的相关统计信息,但是执行计划中的BUFFERS就是真实的逻辑读的多少
                 
--缺陷:1.必须要等到语句真正执行完毕后,才可以出结果。
        2.无法控制记录输屏打出,不像autotrace有 traceonly 可以控制不将结果打屏输出。
        3.看不出递归调用的次数,看不出物理读的多少(不过逻辑读才是重点)
*/



----方法4(知道sql_id后,直接带入的方式,简单,就步骤1)

  
/*  

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

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

*/


select * from table(dbms_xplan.display_cursor('1a914ws3ggfsn'));
PLAN_TABLE_OUTPUT
-------------------------------------------------------------------------------------------
SQL_ID  1a914ws3ggfsn, child number 0
-------------------------------------
SELECT  * FROM t1, t2 WHERE t1.id = t2.t1_id AND t1.n in(18,19)

Plan hash value: 3532430033
-------------------------------------------------------------------------------------------
| Id  | Operation                      | Name     | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT               |          |       |       |     6 (100)|          |
|   1 |  NESTED LOOPS                  |          |       |       |            |          |
|   2 |   NESTED LOOPS                 |          |     2 |  8138 |     6   (0)| 00:00:01 |
|   3 |    INLIST ITERATOR             |          |       |       |            |          |
|   4 |     TABLE ACCESS BY INDEX ROWID| T1       |     2 |  4056 |     2   (0)| 00:00:01 |
|*  5 |      INDEX RANGE SCAN          | T1_N     |     1 |       |     1   (0)| 00:00:01 |
|*  6 |    INDEX RANGE SCAN            | T2_T1_ID |     1 |       |     1   (0)| 00:00:01 |
|   7 |   TABLE ACCESS BY INDEX ROWID  | T2       |     1 |  2041 |     2   (0)| 00:00:01 |
-------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------

   5 - access(("T1"."N"=18 OR "T1"."N"=19))
   6 - access("T1"."ID"="T2"."T1_ID")

Note
-----
   - dynamic sampling used for this statement (level=2)
   
/*   
--优点:1.知道sql_id立即可得到执行计划,和explain plan for 一样无需执行;
        2.可以得到真实的执行计划。(停,等等,啥真实的,刚才这几个套路中,还有假的执行计划的吗?)
        
                 
--缺陷  1.没有输出运行时的相关统计信息(产生多少逻辑读,多少次递归调用,多少次物理读的情况);
        2.无法判断是处理了多少行;  
        3.无法判断表被访问了多少次。
        
*/        

----方法5(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;

exit      
 
tkprof d:\oracle\diag\rdbms\test11g\test11g\trace/test11g_ora_2492.trc    d:\10046.txt  sys=no sort=prsela,exeela,fchela       

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

call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.00       0.00          0          0          0           0
Execute      1      0.00       0.00          0          0          0           0
Fetch        2      0.00       0.00          0         12          0           2
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total        4      0.00       0.00          0         12          0           2

Misses in library cache during parse: 0
Optimizer mode: ALL_ROWS
Parsing user id: 94  

Rows     Row Source Operation
-------  ---------------------------------------------------
      2  NESTED LOOPS  (cr=12 pr=0 pw=0 time=0 us)
      2   NESTED LOOPS  (cr=10 pr=0 pw=0 time=48 us cost=6 size=8138 card=2)
      2    INLIST ITERATOR  (cr=5 pr=0 pw=0 time=16 us)
      2     TABLE ACCESS BY INDEX ROWID T1 (cr=5 pr=0 pw=0 time=0 us cost=2 size=4056 card=2)
      2      INDEX RANGE SCAN T1_N (cr=3 pr=0 pw=0 time=0 us cost=1 size=0 card=1)(object id 108621)
      2    INDEX RANGE SCAN T2_T1_ID (cr=5 pr=0 pw=0 time=0 us cost=1 size=0 card=1)(object id 108622)
      2   TABLE ACCESS BY INDEX ROWID T2 (cr=2 pr=0 pw=0 time=0 us cost=2 size=2041 card=1)


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        1.31          1.31
  
/*
--优点:1.可以看出SQL语句对应的等待事件
        2.如果SQL语句中有函数调用,SQL中有SQL,将会都被列出,无处遁形。
        3.可以方便的看出处理的行数,产生的物理逻辑读。
        4.可以方便的看出解析时间和执行时间。
        5.可以跟踪整个程序包
        
        
                 
--缺陷: 1.步骤繁琐,比较麻烦
        2.无法判断表被访问了多少次。
        3.执行计划中的谓词部分不能清晰的展现出来。
        
*/        
        
6. awrsqrpt.sql   

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

  递归调用:

/*
分结论2(递归的调用):

一. 关于获取执行计划的6种方法和各自区别。

    1. explain plan for获取; 
    2. set autotrace on ;    
    3. statistics_level=all;
    4. 通过dbms_xplan.display_cursor输入sql_id参数直接获取
    5. 10046 trace跟踪
    6. awrsqrpt.sql
    
二. 执行计划中"递归调用” 是关键指标,这只能靠方法2和方法5了。
    方法2的set autotrace on ;和方法5的10046 trace跟踪

*/

drop table people purge;
create table people (first_name varchar2(200),last_name varchar2(200),sex_id number);

create table sex (name varchar2(20), sex_id number);
insert into people (first_name,last_name,sex_id) select object_name,object_type,1 from dba_objects;
insert into sex (name,sex_id) values ('男',1);
insert into sex (name,sex_id) values ('女',2);
insert into sex (name,sex_id) values ('不详',3);
commit;


create or replace function get_sex_name(p_id sex.sex_id%type) return sex.name%type is
v_name sex.name%type;
begin
select name
into v_name
from sex
where sex_id=p_id;
return v_name;
end;
/

/*
  下面我们将会用多种方法来查看如下语句的执行计划
select sex_id,
first_name||' '||last_name full_name,
get_sex_name(sex_id) gender
from people;

*/

----方法1(explain plan for 的方式。类似PLSQL DEVELOPE里的F5)
set linesize 1000
set pagesize 2000
explain plan for
select sex_id,
first_name||' '||last_name full_name,
get_sex_name(sex_id) gender
from people;
select * from table(dbms_xplan.display());
PLAN_TABLE_OUTPUT
----------------------------------------------------------------------------
Plan hash value: 2528372185
----------------------------------------------------------------------------
| Id  | Operation         | Name   | Rows  | Bytes | Cost (%CPU)| Time     |
----------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |        | 80635 |    16M|   137   (1)| 00:00:02 |
|   1 |  TABLE ACCESS FULL| PEOPLE | 80635 |    16M|   137   (1)| 00:00:02 |
----------------------------------------------------------------------------
Note
-----
   - dynamic sampling used for this statement (level=2)

已选择12行。

----方法2(set autotrace on 方式)
/*set autotrace on 
 set autotrace traceonly  
 set autotrace traceonly explain 
 set autotrace traceonl statistics
*/
set autotrace traceonly
select sex_id,
first_name||' '||last_name full_name,
get_sex_name(sex_id) gender
from people;
执行计划
----------------------------------------------------------
Plan hash value: 2528372185
----------------------------------------------------------------------------
| Id  | Operation         | Name   | Rows  | Bytes | Cost (%CPU)| Time     |
----------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |        | 80635 |    16M|   137   (1)| 00:00:02 |
|   1 |  TABLE ACCESS FULL| PEOPLE | 80635 |    16M|   137   (1)| 00:00:02 |
----------------------------------------------------------------------------
Note
-----
   - dynamic sampling used for this statement (level=2)

统计信息
----------------------------------------------------------
      73121  recursive calls
          0  db block gets
     517142  consistent gets
          0  physical reads
          0  redo size
    3382143  bytes sent via SQL*Net to client
      54029  bytes received via SQL*Net from client
       4876  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
      73121  rows processed

        
----方法3(statistics level=all的方式)         
set autotrace off          
alter session set statistics_level=all ;
select sex_id,
first_name||' '||last_name full_name,
get_sex_name(sex_id) gender
from people;
select * from table(dbms_xplan.display_cursor(null,null,'allstats last'));

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------
SQL_ID  f4hgrku2d4usc, child number 1
-------------------------------------
select sex_id, first_name||' '||last_name full_name,
get_sex_name(sex_id) gender from people

Plan hash value: 2528372185
--------------------------------------------------------------------------------------
| Id  | Operation         | Name   | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
--------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |        |      1 |        |  73121 |00:00:00.14 |    5295 |
|   1 |  TABLE ACCESS FULL| PEOPLE |      1 |  80635 |  73121 |00:00:00.14 |    5295 |
--------------------------------------------------------------------------------------
Note
-----
   - dynamic sampling used for this statement (level=2)

已选择18行。


----方法4(知道sql_id后,直接带入的方式)
select * from table(dbms_xplan.display_cursor('f4hgrku2d4usc'));

PLAN_TABLE_OUTPUT
-----------------------------------------------------------------------------
SQL_ID  f4hgrku2d4usc, child number 0
-------------------------------------
select sex_id, first_name||' '||last_name full_name,
get_sex_name(sex_id) gender from people

Plan hash value: 2528372185
----------------------------------------------------------------------------
| Id  | Operation         | Name   | Rows  | Bytes | Cost (%CPU)| Time     |
----------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |        |       |       |   137 (100)|          |
|   1 |  TABLE ACCESS FULL| PEOPLE | 80635 |    16M|   137   (1)| 00:00:02 |
----------------------------------------------------------------------------
Note
-----
   - dynamic sampling used for this statement (level=2)
已选择18行。
   

----方法5(10046TRACE)
set autotace off
alter session set statistics_level=typical;     
alter session set events '10046 trace name context  forever,level 12';

select sex_id,
first_name||' '||last_name full_name,
get_sex_name(sex_id) gender
from people;  
   
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;

exit       
tkprof d:\oracle\diag\rdbms\test11g\test11g\trace/test11g_ora_4824.trc    d:\10046_2.txt  sys=no sort=prsela,exeela,fchela       


--结果发现,其实是SQL_ID=dyspxnp5ndqj1语句成漏网之鱼了。

SQL ID: dyspxnp5ndqj1
Plan Hash: 3096199304
SELECT NAME 
FROM
SEX WHERE SEX_ID=:B1 


call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        0      0.00       0.00          0          0          0           0
Execute  73121      9.48       9.66          0          0          0           0
Fetch    73121      1.54       1.88          0     511847          0       73121
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total   146242     11.02      11.55          0     511847          0       73121

Misses in library cache during parse: 0
Optimizer mode: ALL_ROWS
Parsing user id: 94     (recursive depth: 1)

Elapsed times include waiting on following events:
  Event waited on                             Times   Max. Wait  Total Waited
  ----------------------------------------   Waited  ----------  ------------
  SQL*Net message to client                       1        0.00          0.00
  SQL*Net message from client                     1        0.00          0.00
********************************************************************************

select sex_id,
first_name||' '||last_name full_name,
get_sex_name(sex_id) gender
from people

call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.00       0.00          0         71          0           0
Execute      1      0.00       0.00          0          0          0           0
Fetch     4876      8.68       8.68          0       5302          0       73121
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total     4878      8.68       8.69          0       5373          0       73121

Misses in library cache during parse: 1
Optimizer mode: ALL_ROWS
Parsing user id: 94  

Rows     Row Source Operation
-------  ---------------------------------------------------
  73121  TABLE ACCESS FULL PEOPLE (cr=5295 pr=0 pw=0 time=142014 us cost=137 size=17497795 card=80635)


Elapsed times include waiting on following events:
  Event waited on                             Times   Max. Wait  Total Waited
  ----------------------------------------   Waited  ----------  ------------
  SQL*Net message to client                    4876        0.00          0.02
  SQL*Net message from client                  4876        0.11         50.97
********************************************************************************

  不真实的执行计划:

/*
一.关于获取执行计划的6种方法和各自区别。
    1. explain plan for获取; 
    2. set autotrace on ;    
    3. statistics_level=all;
    4. 通过dbms_xplan.display_cursor输入sql_id参数直接获取
    5. 10046 trace跟踪
    6. awrsqrpt.sql
    
二. 执行计划中"真实执行计划” 是一个很重要的常识,这也就是方法1 和方法2 的最大缺陷了。 
    方法1的explain plan for和方法2的set autotrace on

例子主要是针对:绑定变量窥视与直方图
*/

---构建T表,数据,及主键

DROP TABLE t;
CREATE TABLE t 
AS 
SELECT rownum AS id, rpad('*',100,'*') AS pad 
FROM dual
CONNECT BY level <= 1000;
ALTER TABLE t ADD CONSTRAINT t_pk PRIMARY KEY (id);
---收集统计信息
BEGIN
  dbms_stats.gather_table_stats(
    ownname          => user, 
    tabname          => 'T', 
    estimate_percent => 100, 
    method_opt       => 'for all columns size 254' 
  );
END;
/

/*
  下面我们将会用多种方法来查看如下语句的执行计划
VARIABLE id NUMBER
COLUMN sql_id NEW_VALUE sql_id  
EXECUTE :id := 990;
SELECT count(pad) FROM t WHERE id < :id;
EXECUTE :id := 10;
SELECT count(pad) FROM t WHERE id < :id;

*/

----方法1(explain plan for 的方式。类似PLSQL DEVELOPE里的F5)
set linesize 1000
set pagesize 2000
VARIABLE id NUMBER
COLUMN sql_id NEW_VALUE sql_id
EXECUTE :id := 990;
explain plan for
SELECT count(pad) FROM t WHERE id < :id;
select * from table(dbms_xplan.display());

--明明应该是走全表扫描合适,居然走了索引读,原因是,这个执行计划是假的。
SQL> select * from table(dbms_xplan.display());

PLAN_TABLE_OUTPUT
-------------------------------------------------------------------------------------
Plan hash value: 4270555908
-------------------------------------------------------------------------------------
| Id  | Operation                    | Name | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT             |      |     1 |   105 |     3   (0)| 00:00:01 |
|   1 |  SORT AGGREGATE              |      |     1 |   105 |            |          |
|   2 |   TABLE ACCESS BY INDEX ROWID| T    |    50 |  5250 |     3   (0)| 00:00:01 |
|*  3 |    INDEX RANGE SCAN          | T_PK |     9 |       |     2   (0)| 00:00:01 |
-------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------

   3 - access("ID"<TO_NUMBER(:ID))

已选择15行。

----方法2(set autotrace on 方式)
/*set autotrace on 
 set autotrace traceonly  
 set autotrace traceonly explain 
 set autotrace traceonl statistics
*/
set autotrace traceonly
VARIABLE id NUMBER
COLUMN sql_id NEW_VALUE sql_id
EXECUTE :id := 990;
SELECT count(pad) FROM t WHERE id < :id;

执行计划
----------------------------------------------------------
Plan hash value: 4270555908
-------------------------------------------------------------------------------------
| Id  | Operation                    | Name | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT             |      |     1 |   105 |     3   (0)| 00:00:01 |
|   1 |  SORT AGGREGATE              |      |     1 |   105 |            |          |
|   2 |   TABLE ACCESS BY INDEX ROWID| T    |    50 |  5250 |     3   (0)| 00:00:01 |
|*  3 |    INDEX RANGE SCAN          | T_PK |     9 |       |     2   (0)| 00:00:01 |
-------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   3 - access("ID"<TO_NUMBER(:ID))
统计信息
----------------------------------------------------------
          0  recursive calls
          0  db block gets
         19  consistent gets
          0  physical reads
          0  redo size
        426  bytes sent via SQL*Net to client
        415  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processed
  
----方法3(statistics level=all的方式)         
set autotrace off          
alter session set statistics_level=all ;
VARIABLE id NUMBER
COLUMN sql_id NEW_VALUE sql_id
EXECUTE :id := 990;
SELECT count(pad) FROM t WHERE id < :id;
select * from table(dbms_xplan.display_cursor(null,null,'allstats last'));

PLAN_TABLE_OUTPUT
-------------------------------------------------------------------------------------
SQL_ID  asth1mx10aygn, child number 1
-------------------------------------
SELECT count(pad) FROM t WHERE id < :id

Plan hash value: 2966233522
-------------------------------------------------------------------------------------
| Id  | Operation          | Name | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
-------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |      |      1 |        |      1 |00:00:00.01 |      19 |
|   1 |  SORT AGGREGATE    |      |      1 |      1 |      1 |00:00:00.01 |      19 |
|*  2 |   TABLE ACCESS FULL| T    |      1 |    988 |    989 |00:00:00.01 |      19 |
-------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   2 - filter("ID"<:ID)


已选择19行。

----方法4(知道sql_id后,直接带入的方式)
select * from table(dbms_xplan.display_cursor('asth1mx10aygn'));

PLAN_TABLE_OUTPUT
---------------------------------------------------------------------------
SQL_ID  asth1mx10aygn, child number 0
-------------------------------------
SELECT count(pad) FROM t WHERE id < :id
Plan hash value: 2966233522
---------------------------------------------------------------------------
| Id  | Operation          | Name | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |      |       |       |     7 (100)|          |
|   1 |  SORT AGGREGATE    |      |     1 |   105 |            |          |
|*  2 |   TABLE ACCESS FULL| T    |   988 |   101K|     7   (0)| 00:00:01 |
---------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   2 - filter("ID"<:ID)
已选择19行。
   
----方法5(10046TRACE)
set autotace off
alter session set statistics_level=typical;     
alter session set events '10046 trace name context  forever,level 12';
VARIABLE id NUMBER
COLUMN sql_id NEW_VALUE sql_id
EXECUTE :id := 990;
SELECT count(pad) FROM t WHERE id < :id;
   
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;

exit       
tkprof d:\oracle\diag\rdbms\test11g\test11g\trace/test11g_ora_3144.trc    d:\10046_3.txt  sys=no sort=prsela,exeela,fchela       


--观察发现,也是真实的执行计划,全表扫描!

SQL ID: asth1mx10aygn
Plan Hash: 2966233522
SELECT count(pad) 
FROM
 t WHERE id < :id


call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.00       0.00          0          0          0           0
Execute      1      0.00       0.00          0          0          0           0
Fetch        2      0.00       0.00          0         19          0           1
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total        4      0.00       0.00          0         19          0           1

Misses in library cache during parse: 1
Misses in library cache during execute: 1
Optimizer mode: ALL_ROWS
Parsing user id: 94  

Rows     Row Source Operation
-------  ---------------------------------------------------
      1  SORT AGGREGATE (cr=19 pr=0 pw=0 time=0 us)
    989   TABLE ACCESS FULL T (cr=19 pr=0 pw=0 time=494 us cost=7 size=103740 card=988)


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        8.06          8.06
********************************************************************************

  多执行计划:

/*

分结论4(一条SQL对应多个执行计划):

一. 关于获取执行计划的6种方法和各自区别。
    1. explain plan for获取; 
    2. set autotrace on ;    
    3. statistics_level=all;
    4. 通过dbms_xplan.display_cursor输入sql_id参数直接获取
    5. 10046 trace跟踪
    6. awrsqrpt.sql
    
二. 执行计划中"一条SQL对应多个计划” 也是一个很重要的常识,这只能靠方法4和方法6了。
    :方法4的dbms_xplan.display_cursor+sql_id和方法6的awrsqrpt.sql。
*/

---构建T表,数据,及主键

sqlplus ljb/ljb
DROP TABLE t;
CREATE TABLE t AS SELECT * FROM DBA_OBJECTS where object_id is not null;
create index idx_object_id on t(object_id);
alter table T modify object_id not null;
set autotrace off  
set linesize 1000
set pagesize 2000        
alter session set statistics_level=all ;
select count(*) from t;
select * from table(dbms_xplan.display_cursor(null,null,'allstats last'));

PLAN_TABLE_OUTPUT
----------------------------------------------------------------------------------------------------------
SQL_ID  cyzznbykb509s, child number 0
-------------------------------------
select count(*) from t

Plan hash value: 1131838604
----------------------------------------------------------------------------------------------------------
| Id  | Operation             | Name          | Starts | E-Rows | A-Rows |   A-Time   | Buffers | Reads  |
----------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT      |               |      1 |        |      1 |00:00:00.10 |     170 |    162 |
|   1 |  SORT AGGREGATE       |               |      1 |      1 |      1 |00:00:00.10 |     170 |    162 |
|   2 |   INDEX FAST FULL SCAN| IDX_OBJECT_ID |      1 |  65318 |  73118 |00:00:00.09 |     170 |    162 |
----------------------------------------------------------------------------------------------------------
Note
-----
   - dynamic sampling used for this statement (level=2)
已选择18行。


connect yxl/yxl
drop table t purge;
CREATE TABLE t AS SELECT rownum id ,rownum+1 n FROM DBA_OBJECTS ;
set autotrace off  
set linesize 1000  
set pagesize 2000               
alter session set statistics_level=all ;
select count(*) from t;
select * from table(dbms_xplan.display_cursor(null,null,'allstats last'));
PLAN_TABLE_OUTPUT
-----------------------------------------------------------------------------------------------
SQL_ID  cyzznbykb509s, child number 1
-------------------------------------
select count(*) from t
Plan hash value: 2966233522
----------------------------------------------------------------------------------------------
| Id  | Operation          | Name | Starts | E-Rows | A-Rows |   A-Time   | Buffers | Reads  |
----------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |      |      1 |        |      1 |00:00:00.02 |     153 |     41 |
|   1 |  SORT AGGREGATE    |      |      1 |      1 |      1 |00:00:00.02 |     153 |     41 |
|   2 |   TABLE ACCESS FULL| T    |      1 |  62936 |  73120 |00:00:00.02 |     153 |     41 |
----------------------------------------------------------------------------------------------
Note
-----
   - dynamic sampling used for this statement (level=2)


已选择18行。

----(知道sql_id后,直接带入的方式)
select sql_id, child_number from v$sql  where sql_id='cyzznbykb509s';

SQL_ID        CHILD_NUMBER
------------- ------------
cyzznbykb509s            0
cyzznbykb509s            1


select * from table(dbms_xplan.display_cursor('cyzznbykb509s',0));

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
SQL_ID  cyzznbykb509s, child number 0
-------------------------------------
select count(*) from t

Plan hash value: 1131838604
-------------------------------------------------------------------------------
| Id  | Operation             | Name          | Rows  | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------
|   0 | SELECT STATEMENT      |               |       |    49 (100)|          |
|   1 |  SORT AGGREGATE       |               |     1 |            |          |
|   2 |   INDEX FAST FULL SCAN| IDX_OBJECT_ID | 65318 |    49   (0)| 00:00:01 |
-------------------------------------------------------------------------------
Note
-----
   - dynamic sampling used for this statement (level=2)
已选择18行。

select * from table(dbms_xplan.display_cursor('cyzznbykb509s',1));

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------
SQL_ID  cyzznbykb509s, child number 1
-------------------------------------
select count(*) from t

Plan hash value: 2966233522
-------------------------------------------------------------------
| Id  | Operation          | Name | Rows  | Cost (%CPU)| Time     |
-------------------------------------------------------------------
|   0 | SELECT STATEMENT   |      |       |    46 (100)|          |
|   1 |  SORT AGGREGATE    |      |     1 |            |          |
|   2 |   TABLE ACCESS FULL| T    | 62936 |    46   (3)| 00:00:01 |
-------------------------------------------------------------------
Note
-----
   - dynamic sampling used for this statement (level=2)
已选择18行。


/*
第6种获取执行计划的方法awrsqrpt.sql同样也可以获取到多条执行计划
这个方法当一条SQL有多个执行计划的时候,可以在报表里输出。但是要确保在AWR的采集周期内的生成报表。
*/

  获取执行计划的6种方法

1. explain plan for获取;
2. set autotrace on ;
3. statistics_level=all;
4. 通过dbms_xplan.display_cursor输入sql_id参数直接获取
5. 10046 trace跟踪
6. awrsqrpt.sql

 6种方法各自适用场合

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

 

单独型:

DROP TABLE bonus cascade constraints PURGE;
DROP TABLE emp cascade constraints PURGE;
DROP TABLE dept cascade constraints PURGE;

CREATE TABLE dept
    (deptno NUMBER(2),
     dname VARCHAR2(14),
     loc VARCHAR2(13) );

INSERT INTO dept VALUES (10, 'ACCOUNTING', 'NEW YORK');
INSERT INTO dept VALUES (20, 'RESEARCH', 'DALLAS');
INSERT INTO dept VALUES (30, 'SALES',   'CHICAGO');
INSERT INTO dept VALUES (40, 'OPERATIONS', 'BOSTON');

ALTER TABLE dept ADD CONSTRAINT dept_pk PRIMARY KEY (deptno);

execute dbms_stats.gather_table_stats(user, 'dept')

CREATE TABLE emp
    (empno NUMBER(4) NOT NULL,
     ename VARCHAR2(10),
     job VARCHAR2(9),
     mgr NUMBER(4),
     hiredate DATE,
     sal NUMBER(7, 2),
     comm NUMBER(7, 2),
     deptno NUMBER(2));

INSERT INTO emp VALUES (7369, 'SMITH', 'CLERK', 7902, to_date('17-04-1980', 'DD-MM-YYYY'), 800, NULL, 20);
INSERT INTO emp VALUES (7499, 'ALLEN', 'SALESMAN', 7698, to_date('20-05-1981', 'DD-MM-YYYY'), 1600, 300, 30);
INSERT INTO emp VALUES (7521, 'WARD', 'SALESMAN', 7698, to_date('22-06-1981', 'DD-MM-YYYY'), 1250, 500, 30);
INSERT INTO emp VALUES (7566, 'JONES', 'MANAGER', 7839, to_date('2-07-1981', 'DD-MM-YYYY'), 2975, NULL, 20);
INSERT INTO emp VALUES (7654, 'MARTIN', 'SALESMAN', 7698, to_date('28-08-1981', 'DD-MM-YYYY'), 1250, 1400, 30);
INSERT INTO emp VALUES (7698, 'BLAKE', 'MANAGER', 7839, to_date('1-09-1981', 'DD-MM-YYYY'), 2850, NULL, 30);
INSERT INTO emp VALUES (7782, 'CLARK', 'MANAGER', 7839, to_date('9-10-1981', 'DD-MM-YYYY'), 2450, NULL, 10);
INSERT INTO emp VALUES (7788, 'SCOTT', 'ANALYST', 7566, to_date('09-02-1982', 'DD-MM-YYYY'), 3000, NULL, 20);
INSERT INTO emp VALUES (7839, 'KING', 'PRESIDENT', NULL, to_date('17-03-1981', 'DD-MM-YYYY'), 5000, NULL, 10);
INSERT INTO emp VALUES (7844, 'TURNER', 'SALESMAN', 7698, to_date('8-09-1981', 'DD-MM-YYYY'), 1500, 0, 30);
INSERT INTO emp VALUES (7876, 'ADAMS', 'CLERK',  7788, to_date('12-08-1983', 'DD-MM-YYYY'), 1100, NULL, 20);
INSERT INTO emp VALUES (7900, 'JAMES', 'CLERK',  7698, to_date('3-11-1981', 'DD-MM-YYYY'), 950, NULL, 30);
INSERT INTO emp VALUES (7902, 'FORD', 'ANALYST', 7566, to_date('3-12-1981', 'DD-MM-YYYY'), 3000, NULL, 20);
INSERT INTO emp VALUES (7934, 'MILLER', 'CLERK',  7782, to_date('23-10-1982', 'DD-MM-YYYY'), 1300, NULL, 10);

ALTER TABLE emp ADD CONSTRAINT emp_pk PRIMARY KEY (empno);
ALTER TABLE emp ADD CONSTRAINT emp_dept_pk FOREIGN KEY (deptno) REFERENCING DEPT (deptno);

CREATE INDEX emp_job_i ON emp (job);
CREATE INDEX emp_mgr_i ON emp (mgr);

execute dbms_stats.gather_table_stats(user, 'emp')

CREATE TABLE bonus
     (ename VARCHAR2(10),
      job VARCHAR2(9),
      sal NUMBER,
      comm NUMBER);

execute dbms_stats.gather_table_stats(user, 'bonus');

set linesize 1000
set pagesize 2000
set autotrace off
ALTER SESSION SET statistics_level = all;

REM
--单独形
REM

SELECT deptno, count(*)
FROM emp
WHERE job = 'CLERK' AND sal < 1200
GROUP BY deptno;

SELECT * FROM table(dbms_xplan.display_cursor(null,null,'allstats last'));

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------------------------------------------
SQL_ID  5k3rhmx9hprh7, child number 1
-------------------------------------
SELECT deptno, count(*) FROM emp WHERE job = 'CLERK' AND sal < 1200
GROUP BY deptno

Plan hash value: 3067371962
-------------------------------------------------------------------------------------------------------------------------------
| Id  | Operation                    | Name      | Starts | E-Rows | A-Rows |   A-Time   | Buffers |  OMem |  1Mem | Used-Mem |
-------------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT             |           |      1 |        |      2 |00:00:00.01 |       2 |    |          |          |
|   1 |  HASH GROUP BY               |           |      1 |      1 |      2 |00:00:00.01 |       2 |   888K|   888K|  539K (0)|
|*  2 |   TABLE ACCESS BY INDEX ROWID| EMP       |      1 |      3 |      3 |00:00:00.01 |       2 |    |          |          |
|*  3 |    INDEX RANGE SCAN          | EMP_JOB_I |      1 |      4 |      4 |00:00:00.01 |       1 |    |          |          |
-------------------------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   2 - filter("SAL"<1200)
   3 - access("JOB"='CLERK')
Note
-----
   - cardinality feedback used for this statement


已选择26行。


---也可试set autotrace 的获取方法
set autotrace traceonly
set linesize 1000
set pagesize 2000
SELECT deptno, count(*)
FROM emp
WHERE job = 'CLERK' AND sal < 1200
GROUP BY deptno;

执行计划
----------------------------------------------------------
Plan hash value: 3067371962
------------------------------------------------------------------------------------------
| Id  | Operation                    | Name      | Rows  | Bytes | Cost (%CPU)| Time     |
------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT             |           |     1 |    15 |     3  (34)| 00:00:01 |
|   1 |  HASH GROUP BY               |           |     1 |    15 |     3  (34)| 00:00:01 |
|*  2 |   TABLE ACCESS BY INDEX ROWID| EMP       |     1 |    15 |     2   (0)| 00:00:01 |
|*  3 |    INDEX RANGE SCAN          | EMP_JOB_I |     3 |       |     1   (0)| 00:00:01 |
------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   2 - filter("SAL"<1200)
   3 - access("JOB"='CLERK')
统计信息
----------------------------------------------------------
          0  recursive calls
          0  db block gets
          2  consistent gets
          0  physical reads
          0  redo size
        527  bytes sent via SQL*Net to client
        416  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          2  rows processed

 联合型(非关联) 

/*
  我们把有多个孩子的操作定义为联合型操作,其中孩子的操作互相独立,我们定义为非关联联合型操作
*/

DROP TABLE bonus cascade constraints PURGE;
DROP TABLE emp cascade constraints PURGE;
DROP TABLE dept cascade constraints PURGE;

CREATE TABLE dept
    (deptno NUMBER(2),
     dname VARCHAR2(14),
     loc VARCHAR2(13) );

INSERT INTO dept VALUES (10, 'ACCOUNTING', 'NEW YORK');
INSERT INTO dept VALUES (20, 'RESEARCH', 'DALLAS');
INSERT INTO dept VALUES (30, 'SALES',   'CHICAGO');
INSERT INTO dept VALUES (40, 'OPERATIONS', 'BOSTON');

ALTER TABLE dept ADD CONSTRAINT dept_pk PRIMARY KEY (deptno);

execute dbms_stats.gather_table_stats(user, 'dept')

CREATE TABLE emp
    (empno NUMBER(4) NOT NULL,
     ename VARCHAR2(10),
     job VARCHAR2(9),
     mgr NUMBER(4),
     hiredate DATE,
     sal NUMBER(7, 2),
     comm NUMBER(7, 2),
     deptno NUMBER(2));

INSERT INTO emp VALUES (7369, 'SMITH', 'CLERK', 7902, to_date('17-04-1980', 'DD-MM-YYYY'), 800, NULL, 20);
INSERT INTO emp VALUES (7499, 'ALLEN', 'SALESMAN', 7698, to_date('20-05-1981', 'DD-MM-YYYY'), 1600, 300, 30);
INSERT INTO emp VALUES (7521, 'WARD', 'SALESMAN', 7698, to_date('22-06-1981', 'DD-MM-YYYY'), 1250, 500, 30);
INSERT INTO emp VALUES (7566, 'JONES', 'MANAGER', 7839, to_date('2-07-1981', 'DD-MM-YYYY'), 2975, NULL, 20);
INSERT INTO emp VALUES (7654, 'MARTIN', 'SALESMAN', 7698, to_date('28-08-1981', 'DD-MM-YYYY'), 1250, 1400, 30);
INSERT INTO emp VALUES (7698, 'BLAKE', 'MANAGER', 7839, to_date('1-09-1981', 'DD-MM-YYYY'), 2850, NULL, 30);
INSERT INTO emp VALUES (7782, 'CLARK', 'MANAGER', 7839, to_date('9-10-1981', 'DD-MM-YYYY'), 2450, NULL, 10);
INSERT INTO emp VALUES (7788, 'SCOTT', 'ANALYST', 7566, to_date('09-02-1982', 'DD-MM-YYYY'), 3000, NULL, 20);
INSERT INTO emp VALUES (7839, 'KING', 'PRESIDENT', NULL, to_date('17-03-1981', 'DD-MM-YYYY'), 5000, NULL, 10);
INSERT INTO emp VALUES (7844, 'TURNER', 'SALESMAN', 7698, to_date('8-09-1981', 'DD-MM-YYYY'), 1500, 0, 30);
INSERT INTO emp VALUES (7876, 'ADAMS', 'CLERK',  7788, to_date('12-08-1983', 'DD-MM-YYYY'), 1100, NULL, 20);
INSERT INTO emp VALUES (7900, 'JAMES', 'CLERK',  7698, to_date('3-11-1981', 'DD-MM-YYYY'), 950, NULL, 30);
INSERT INTO emp VALUES (7902, 'FORD', 'ANALYST', 7566, to_date('3-12-1981', 'DD-MM-YYYY'), 3000, NULL, 20);
INSERT INTO emp VALUES (7934, 'MILLER', 'CLERK',  7782, to_date('23-10-1982', 'DD-MM-YYYY'), 1300, NULL, 10);

ALTER TABLE emp ADD CONSTRAINT emp_pk PRIMARY KEY (empno);
ALTER TABLE emp ADD CONSTRAINT emp_dept_pk FOREIGN KEY (deptno) REFERENCING DEPT (deptno);

CREATE INDEX emp_job_i ON emp (job);
CREATE INDEX emp_mgr_i ON emp (mgr);

execute dbms_stats.gather_table_stats(user, 'emp')

CREATE TABLE bonus
     (ename VARCHAR2(10),
      job VARCHAR2(9),
      sal NUMBER,
      comm NUMBER);

execute dbms_stats.gather_table_stats(user, 'bonus');


set linesize 1000
set pagesize 2000
set autotrace off
ALTER SESSION SET statistics_level = all;


PAUSe

REM
REM Unrelated-Combine Operations
--非关联联合型
REM

SELECT ename FROM emp
UNION ALL
SELECT dname FROM dept
UNION ALL
SELECT '%' FROM dual;

SELECT * FROM table(dbms_xplan.display_cursor(null,null,'allstats last'));

PLAN_TABLE_OUTPUT
-------------------------------------------------------------------------------------
SQL_ID  781xq971h0y2p, child number 0
-------------------------------------
SELECT ename FROM emp UNION ALL SELECT dname FROM dept UNION ALL SELECT
'%' FROM dual
Plan hash value: 4181933179
-------------------------------------------------------------------------------------
| Id  | Operation          | Name | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
-------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |      |      1 |        |     19 |00:00:00.01 |      16 |
|   1 |  UNION-ALL         |      |      1 |        |     19 |00:00:00.01 |      16 |
|   2 |   TABLE ACCESS FULL| EMP  |      1 |     14 |     14 |00:00:00.01 |       8 |
|   3 |   TABLE ACCESS FULL| DEPT |      1 |      4 |      4 |00:00:00.01 |       8 |
|   4 |   FAST DUAL        |      |      1 |      1 |      1 |00:00:00.01 |       0 |
-------------------------------------------------------------------------------------
已选择17行。

  联合型(相关联)01_nl:

/*
  我们把有多个孩子的操作定义为联合型操作,其中一个孩子控制其他孩子的操作我们定义为关联联合型操作
  让我们来看相关联联合型中的NL连接吧。
  先访问的表返回多少条记录,后访问的表就被访问多少次,请注意和FLITER的差异。
*/


DROP TABLE bonus cascade constraints PURGE;
DROP TABLE emp cascade constraints PURGE;
DROP TABLE dept cascade constraints PURGE;

CREATE TABLE dept
    (deptno NUMBER(2),
     dname VARCHAR2(14),
     loc VARCHAR2(13) );

INSERT INTO dept VALUES (10, 'ACCOUNTING', 'NEW YORK');
INSERT INTO dept VALUES (20, 'RESEARCH', 'DALLAS');
INSERT INTO dept VALUES (30, 'SALES',   'CHICAGO');
INSERT INTO dept VALUES (40, 'OPERATIONS', 'BOSTON');

ALTER TABLE dept ADD CONSTRAINT dept_pk PRIMARY KEY (deptno);

execute dbms_stats.gather_table_stats(user, 'dept')

CREATE TABLE emp
    (empno NUMBER(4) NOT NULL,
     ename VARCHAR2(10),
     job VARCHAR2(9),
     mgr NUMBER(4),
     hiredate DATE,
     sal NUMBER(7, 2),
     comm NUMBER(7, 2),
     deptno NUMBER(2));

INSERT INTO emp VALUES (7369, 'SMITH', 'CLERK', 7902, to_date('17-04-1980', 'DD-MM-YYYY'), 800, NULL, 20);
INSERT INTO emp VALUES (7499, 'ALLEN', 'SALESMAN', 7698, to_date('20-05-1981', 'DD-MM-YYYY'), 1600, 300, 30);
INSERT INTO emp VALUES (7521, 'WARD', 'SALESMAN', 7698, to_date('22-06-1981', 'DD-MM-YYYY'), 1250, 500, 30);
INSERT INTO emp VALUES (7566, 'JONES', 'MANAGER', 7839, to_date('2-07-1981', 'DD-MM-YYYY'), 2975, NULL, 20);
INSERT INTO emp VALUES (7654, 'MARTIN', 'SALESMAN', 7698, to_date('28-08-1981', 'DD-MM-YYYY'), 1250, 1400, 30);
INSERT INTO emp VALUES (7698, 'BLAKE', 'MANAGER', 7839, to_date('1-09-1981', 'DD-MM-YYYY'), 2850, NULL, 30);
INSERT INTO emp VALUES (7782, 'CLARK', 'MANAGER', 7839, to_date('9-10-1981', 'DD-MM-YYYY'), 2450, NULL, 10);
INSERT INTO emp VALUES (7788, 'SCOTT', 'ANALYST', 7566, to_date('09-02-1982', 'DD-MM-YYYY'), 3000, NULL, 20);
INSERT INTO emp VALUES (7839, 'KING', 'PRESIDENT', NULL, to_date('17-03-1981', 'DD-MM-YYYY'), 5000, NULL, 10);
INSERT INTO emp VALUES (7844, 'TURNER', 'SALESMAN', 7698, to_date('8-09-1981', 'DD-MM-YYYY'), 1500, 0, 30);
INSERT INTO emp VALUES (7876, 'ADAMS', 'CLERK',  7788, to_date('12-08-1983', 'DD-MM-YYYY'), 1100, NULL, 20);
INSERT INTO emp VALUES (7900, 'JAMES', 'CLERK',  7698, to_date('3-11-1981', 'DD-MM-YYYY'), 950, NULL, 30);
INSERT INTO emp VALUES (7902, 'FORD', 'ANALYST', 7566, to_date('3-12-1981', 'DD-MM-YYYY'), 3000, NULL, 20);
INSERT INTO emp VALUES (7934, 'MILLER', 'CLERK',  7782, to_date('23-10-1982', 'DD-MM-YYYY'), 1300, NULL, 10);

ALTER TABLE emp ADD CONSTRAINT emp_pk PRIMARY KEY (empno);
ALTER TABLE emp ADD CONSTRAINT emp_dept_pk FOREIGN KEY (deptno) REFERENCING DEPT (deptno);

CREATE INDEX emp_job_i ON emp (job);
CREATE INDEX emp_mgr_i ON emp (mgr);

execute dbms_stats.gather_table_stats(user, 'emp')

CREATE TABLE bonus
     (ename VARCHAR2(10),
      job VARCHAR2(9),
      sal NUMBER,
      comm NUMBER);

execute dbms_stats.gather_table_stats(user, 'bonus');

set linesize 1000
set pagesize 2000
set autotrace off
ALTER SESSION SET statistics_level = all;

PAUSE

REM
REM Related-Combine Operations
--关联联合型
REM

REM Operation NESTED LOOPS

SELECT /*+ ordered use_nl(dept) index(dept) */ *
FROM emp, dept
WHERE emp.deptno = dept.deptno
AND emp.comm IS NULL
AND dept.dname != 'SALES';

SELECT * FROM table(dbms_xplan.display_cursor(null,null,'allstats last'));

PLAN_TABLE_OUTPUT
---------------------------------------------------------------------------------------------------
SQL_ID  3ux4g340c933p, child number 0
-------------------------------------
SELECT /*+ ordered use_nl(dept) index(dept) */ * FROM emp, dept WHERE
emp.deptno = dept.deptno AND emp.comm IS NULL AND dept.dname != 'SALES'
--11g执行计划---
Plan hash value: 2694310824
--------------------------------------------------------------------------------------------------
| Id  | Operation                    | Name    | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
--------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT             |         |      1 |        |      8 |00:00:00.01 |      22 |
|   1 |  NESTED LOOPS                |         |      1 |        |      8 |00:00:00.01 |      22 |
|   2 |   NESTED LOOPS               |         |      1 |     10 |     10 |00:00:00.01 |      12 |
|*  3 |    TABLE ACCESS FULL         | EMP     |      1 |     10 |     10 |00:00:00.01 |       8 |
|*  4 |    INDEX UNIQUE SCAN         | DEPT_PK |     10 |      1 |     10 |00:00:00.01 |       4 |
|*  5 |   TABLE ACCESS BY INDEX ROWID| DEPT    |     10 |      1 |      8 |00:00:00.01 |      10 |
--------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   3 - filter("EMP"."COMM" IS NULL)
   4 - access("EMP"."DEPTNO"="DEPT"."DEPTNO")
   5 - filter("DEPT"."DNAME"<>'SALES')


已选择25行。


--请注意在Oracle10g的执行计划:
SQL> SELECT * FROM table(dbms_xplan.display_cursor(null,null,'allstats last'));
PLAN_TABLE_OUTPUT
-------------------------------------------------------------------------------------------------
SQL_ID  3ux4g340c933p, child number 0
-------------------------------------
SELECT /*+ ordered use_nl(dept) index(dept) */ * FROM emp, dept WHERE emp.deptno =
dept.deptno AND emp.comm IS NULL AND dept.dname != 'SALES'
--10g执行计划---
Plan hash value: 3487251775
--------------------------------------------------------------------------------------------------
| Id  | Operation                    | Name    | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
--------------------------------------------------------------------------------------------------
|   1 |  NESTED LOOPS                |         |      1 |     10 |      8 |00:00:00.01 |      20 |
|*  2 |   TABLE ACCESS FULL          | EMP     |      1 |     10 |     10 |00:00:00.01 |       8 |
|*  3 |   TABLE ACCESS BY INDEX ROWID| DEPT    |     10 |      1 |      8 |00:00:00.01 |      12 |
|*  4 |    INDEX UNIQUE SCAN         | DEPT_PK |     10 |      1 |     10 |00:00:00.01 |       2 |
--------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   2 - filter("EMP"."COMM" IS NULL)
   3 - filter("DEPT"."DNAME"<>'SALES')
   4 - access("EMP"."DEPTNO"="DEPT"."DEPTNO")

23 rows selected.

  11g中的rowid的访问优化提升分析比较:

/*

   结论:用rowid读取同一个block的30行,在11g中仅仅耗费了3个逻辑读,而在10g中耗费30个逻辑读。 
   说明11g引入了对rowid读的新特性, 那就是,如果rowid是来自一个block,那么是可以重用的!  
            
*/

---11g的情况

drop table t purge;
create table t as select * from dba_objects;

SELECT  DBMS_ROWID.rowid_relative_fno (ROWID) REL_FNO,
        DBMS_ROWID.rowid_block_number (ROWID) BLOCKNO,
        DBMS_ROWID.rowid_row_number (ROWID) ROWNO,
        ROWID
   FROM T WHERE ROWNUM<=30;

  REL_FNO    BLOCKNO      ROWNO ROWID
--------- ---------- ---------- ------------------
       11     284259          0 AAAak/AALAABFZjAAA
       11     284259          1 AAAak/AALAABFZjAAB
       11     284259          2 AAAak/AALAABFZjAAC
       11     284259          3 AAAak/AALAABFZjAAD
       11     284259          4 AAAak/AALAABFZjAAE
       11     284259          5 AAAak/AALAABFZjAAF
       11     284259          6 AAAak/AALAABFZjAAG
       11     284259          7 AAAak/AALAABFZjAAH
       11     284259          8 AAAak/AALAABFZjAAI
       11     284259          9 AAAak/AALAABFZjAAJ
       11     284259         10 AAAak/AALAABFZjAAK
       11     284259         11 AAAak/AALAABFZjAAL
       11     284259         12 AAAak/AALAABFZjAAM
       11     284259         13 AAAak/AALAABFZjAAN
       11     284259         14 AAAak/AALAABFZjAAO
       11     284259         15 AAAak/AALAABFZjAAP
       11     284259         16 AAAak/AALAABFZjAAQ
       11     284259         17 AAAak/AALAABFZjAAR
       11     284259         18 AAAak/AALAABFZjAAS
       11     284259         19 AAAak/AALAABFZjAAT
       11     284259         20 AAAak/AALAABFZjAAU
       11     284259         21 AAAak/AALAABFZjAAV
       11     284259         22 AAAak/AALAABFZjAAW
       11     284259         23 AAAak/AALAABFZjAAX
       11     284259         24 AAAak/AALAABFZjAAY
       11     284259         25 AAAak/AALAABFZjAAZ
       11     284259         26 AAAak/AALAABFZjAAa
       11     284259         27 AAAak/AALAABFZjAAb
       11     284259         28 AAAak/AALAABFZjAAc
       11     284259         29 AAAak/AALAABFZjAAd

已选择30行。


set linesize 1000
set pagesize 2000
set autotrace traceonly

select * from t 
where rowid in ('AAAak/AALAABFZjAAA',
                'AAAak/AALAABFZjAAB',
                'AAAak/AALAABFZjAAC',
                'AAAak/AALAABFZjAAD',
                'AAAak/AALAABFZjAAE',
                'AAAak/AALAABFZjAAF',
                'AAAak/AALAABFZjAAG',
                'AAAak/AALAABFZjAAH',
                'AAAak/AALAABFZjAAI',
                'AAAak/AALAABFZjAAJ',
                'AAAak/AALAABFZjAAK',
                'AAAak/AALAABFZjAAL',
                'AAAak/AALAABFZjAAM',
                'AAAak/AALAABFZjAAN',
                'AAAak/AALAABFZjAAO',
                'AAAak/AALAABFZjAAP',
                'AAAak/AALAABFZjAAQ',
                'AAAak/AALAABFZjAAR',
                'AAAak/AALAABFZjAAS',
                'AAAak/AALAABFZjAAT',
                'AAAak/AALAABFZjAAU',
                'AAAak/AALAABFZjAAV',
                'AAAak/AALAABFZjAAW',
                'AAAak/AALAABFZjAAX',
                'AAAak/AALAABFZjAAY',
                'AAAak/AALAABFZjAAZ',
                'AAAak/AALAABFZjAAa',
                'AAAak/AALAABFZjAAb',
                'AAAak/AALAABFZjAAc',
                'AAAak/AALAABFZjAAd');
                
执行计划
----------------------------------------------------------
Plan hash value: 749296034
------------------------------------------------------------------------------------
| Id  | Operation                   | Name | Rows  | Bytes | Cost (%CPU)| Time     |
------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |      |     1 |   219 |     1   (0)| 00:00:01 |
|   1 |  INLIST ITERATOR            |      |       |       |            |          |
|   2 |   TABLE ACCESS BY USER ROWID| T    |     1 |   219 |     1   (0)| 00:00:01 |
------------------------------------------------------------------------------------
Note
-----
   - dynamic sampling used for this statement (level=2)
统计信息
----------------------------------------------------------
          0  recursive calls
          0  db block gets
          3  consistent gets
          0  physical reads
          0  redo size
       2381  bytes sent via SQL*Net to client
        426  bytes received via SQL*Net from client
          3  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
         30  rows processed             
 
--研究10g的情况
SELECT  DBMS_ROWID.rowid_relative_fno (ROWID) REL_FNO,
        DBMS_ROWID.rowid_block_number (ROWID) BLOCKNO,
        DBMS_ROWID.rowid_row_number (ROWID) ROWNO,
        ROWID
   FROM T WHERE ROWNUM<=30;
  REL_FNO    BLOCKNO      ROWNO ROWID
---------- ---------- ---------- ------------------
         4        212          0 AACSVaAAEAAAADUAAA
         4        212          1 AACSVaAAEAAAADUAAB
         4        212          2 AACSVaAAEAAAADUAAC
         4        212          3 AACSVaAAEAAAADUAAD
         4        212          4 AACSVaAAEAAAADUAAE
         4        212          5 AACSVaAAEAAAADUAAF
         4        212          6 AACSVaAAEAAAADUAAG
         4        212          7 AACSVaAAEAAAADUAAH
         4        212          8 AACSVaAAEAAAADUAAI
         4        212          9 AACSVaAAEAAAADUAAJ
         4        212         10 AACSVaAAEAAAADUAAK
         4        212         11 AACSVaAAEAAAADUAAL
         4        212         12 AACSVaAAEAAAADUAAM
         4        212         13 AACSVaAAEAAAADUAAN
         4        212         14 AACSVaAAEAAAADUAAO
         4        212         15 AACSVaAAEAAAADUAAP
         4        212         16 AACSVaAAEAAAADUAAQ
         4        212         17 AACSVaAAEAAAADUAAR
         4        212         18 AACSVaAAEAAAADUAAS
         4        212         19 AACSVaAAEAAAADUAAT
         4        212         20 AACSVaAAEAAAADUAAU
         4        212         21 AACSVaAAEAAAADUAAV
         4        212         22 AACSVaAAEAAAADUAAW
         4        212         23 AACSVaAAEAAAADUAAX
         4        212         24 AACSVaAAEAAAADUAAY
         4        212         25 AACSVaAAEAAAADUAAZ
         4        212         26 AACSVaAAEAAAADUAAa
         4        212         27 AACSVaAAEAAAADUAAb
         4        212         28 AACSVaAAEAAAADUAAc
         4        212         29 AACSVaAAEAAAADUAAd

30 rows selected.


set linesize 1000
set pagesize 2000
set autotrace traceonly

select * from t 
where rowid in ('AACSVaAAEAAAADUAAA',            
                'AACSVaAAEAAAADUAAB',
                'AACSVaAAEAAAADUAAC',
                'AACSVaAAEAAAADUAAD',
                'AACSVaAAEAAAADUAAE',
                'AACSVaAAEAAAADUAAF',
                'AACSVaAAEAAAADUAAG',
                'AACSVaAAEAAAADUAAH',
                'AACSVaAAEAAAADUAAI',
                'AACSVaAAEAAAADUAAJ',
                'AACSVaAAEAAAADUAAK',
                'AACSVaAAEAAAADUAAL',
                'AACSVaAAEAAAADUAAM',
                'AACSVaAAEAAAADUAAN',
                'AACSVaAAEAAAADUAAO',
                'AACSVaAAEAAAADUAAP',
                'AACSVaAAEAAAADUAAQ',
                'AACSVaAAEAAAADUAAR',
                'AACSVaAAEAAAADUAAS',
                'AACSVaAAEAAAADUAAT',
                'AACSVaAAEAAAADUAAU',
                'AACSVaAAEAAAADUAAV',
                'AACSVaAAEAAAADUAAW',
                'AACSVaAAEAAAADUAAX',
                'AACSVaAAEAAAADUAAY',
                'AACSVaAAEAAAADUAAZ',
                'AACSVaAAEAAAADUAAa',
                'AACSVaAAEAAAADUAAb',
                'AACSVaAAEAAAADUAAc',
                'AACSVaAAEAAAADUAAd' );
                

Execution Plan
----------------------------------------------------------
Plan hash value: 749296034

------------------------------------------------------------------------------------
| Id  | Operation                   | Name | Rows  | Bytes | Cost (%CPU)| Time     |
------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |      |     1 |   189 |     1   (0)| 00:00:01 |
|   1 |  INLIST ITERATOR            |      |       |       |            |          |
|   2 |   TABLE ACCESS BY USER ROWID| T    |     1 |   189 |     1   (0)| 00:00:01 |
------------------------------------------------------------------------------------
Note
-----
   - dynamic sampling used for this statement
Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
         30  consistent gets
          0  physical reads
          0  redo size
       2386  bytes sent via SQL*Net to client
        503  bytes received via SQL*Net from client
          3  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
         30  rows processed

  11g中的rowid优化引发的执行计划优化:

--环境构造
--研究Nested Loops Join访问次数前准备工作
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 <= 100
      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);

--11g环境

set linesize 1000
set pagesize 2000
alter session set statistics_level=all ;
SELECT  *
FROM t1, t2
WHERE t1.id = t2.t1_id
AND t1.n = 19;
select * from table(dbms_xplan.display_cursor(null,null,'allstats last'));

PLAN_TABLE_OUTPUT
-----------------------------------------------------------------------------------------------------------
SQL_ID  7xxxx7qhk4wvx, child number 0
-------------------------------------
SELECT  * FROM t1, t2 WHERE t1.id = t2.t1_id AND t1.n = 19

Plan hash value: 1300700041
-----------------------------------------------------------------------------------------------------------
| Id  | Operation                     | Name     | Starts | E-Rows | A-Rows |   A-Time   | Buffers | Reads|
-----------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT              |          |      1 |        |      1 |00:00:00.01 |       7 |    4 |
|   1 |  NESTED LOOPS                 |          |      1 |        |      1 |00:00:00.01 |       7 |    4 |
|   2 |   NESTED LOOPS                |          |      1 |      1 |      1 |00:00:00.01 |       6 |    4 |
|   3 |    TABLE ACCESS BY INDEX ROWID| T1       |      1 |      1 |      1 |00:00:00.01 |       3 |    0 |
|*  4 |     INDEX RANGE SCAN          | T1_N     |      1 |      1 |      1 |00:00:00.01 |       2 |    0 |
|*  5 |    INDEX RANGE SCAN           | T2_T1_ID |      1 |      1 |      1 |00:00:00.01 |       3 |    4 |
|   6 |   TABLE ACCESS BY INDEX ROWID | T2       |      1 |      1 |      1 |00:00:00.01 |       1 |    0 |
-----------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   4 - access("T1"."N"=19)
   5 - access("T1"."ID"="T2"."T1_ID")
Note
-----
   - dynamic sampling used for this statement (level=2)

已选择28行。


--10g环境

set linesize 1000
set pagesize 2000
alter session set statistics_level=all ;
SELECT  *
FROM t1, t2
WHERE t1.id = t2.t1_id
AND t1.n = 19;
select * from table(dbms_xplan.display_cursor(null,null,'allstats last'));


PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------------------------
SQL_ID  7xxxx7qhk4wvx, child number 0
-------------------------------------
SELECT  * FROM t1, t2 WHERE t1.id = t2.t1_id AND t1.n = 19

Plan hash value: 2669480776
-------------------------------------------------------------------------------------------------------------
| Id  | Operation                     | Name     | Starts | E-Rows | A-Rows |   A-Time   | Buffers | Reads  |
-------------------------------------------------------------------------------------------------------------
|   1 |  TABLE ACCESS BY INDEX ROWID  | T2       |      1 |      1 |      1 |00:00:00.01 |       7 |      1 |
|   2 |   NESTED LOOPS                |          |      1 |      1 |      3 |00:00:00.01 |       6 |      1 |
|   3 |    TABLE ACCESS BY INDEX ROWID| T1       |      1 |      1 |      1 |00:00:00.01 |       3 |      0 |
|*  4 |     INDEX RANGE SCAN          | T1_N     |      1 |      1 |      1 |00:00:00.01 |       2 |      0 |
|*  5 |    INDEX RANGE SCAN           | T2_T1_ID |      1 |      1 |      1 |00:00:00.01 |       3 |      1 |
-------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   4 - access("T1"."N"=19)
   5 - access("T1"."ID"="T2"."T1_ID")
Note
-----
   - dynamic sampling used for this statement

26 rows selected.

 联合型(相关联)02——filter:

/*
  我们把有多个孩子的操作定义为联合型操作,其中一个孩子控制其他孩子的操作我们定义为关联联合型操作
  先访问的表返回多少不重复记录,后访问的表就访问多少次,请注意这个不重复的关键字,明确和NL的差异。
*/

DROP TABLE bonus cascade constraints PURGE;
DROP TABLE emp cascade constraints PURGE;
DROP TABLE dept cascade constraints PURGE;

CREATE TABLE dept
    (deptno NUMBER(2),
     dname VARCHAR2(14),
     loc VARCHAR2(13) );

INSERT INTO dept VALUES (10, 'ACCOUNTING', 'NEW YORK');
INSERT INTO dept VALUES (20, 'RESEARCH', 'DALLAS');
INSERT INTO dept VALUES (30, 'SALES',   'CHICAGO');
INSERT INTO dept VALUES (40, 'OPERATIONS', 'BOSTON');

ALTER TABLE dept ADD CONSTRAINT dept_pk PRIMARY KEY (deptno);

execute dbms_stats.gather_table_stats(user, 'dept')

CREATE TABLE emp
    (empno NUMBER(4) NOT NULL,
     ename VARCHAR2(10),
     job VARCHAR2(9),
     mgr NUMBER(4),
     hiredate DATE,
     sal NUMBER(7, 2),
     comm NUMBER(7, 2),
     deptno NUMBER(2));

INSERT INTO emp VALUES (7369, 'SMITH', 'CLERK', 7902, to_date('17-04-1980', 'DD-MM-YYYY'), 800, NULL, 20);
INSERT INTO emp VALUES (7499, 'ALLEN', 'SALESMAN', 7698, to_date('20-05-1981', 'DD-MM-YYYY'), 1600, 300, 30);
INSERT INTO emp VALUES (7521, 'WARD', 'SALESMAN', 7698, to_date('22-06-1981', 'DD-MM-YYYY'), 1250, 500, 30);
INSERT INTO emp VALUES (7566, 'JONES', 'MANAGER', 7839, to_date('2-07-1981', 'DD-MM-YYYY'), 2975, NULL, 20);
INSERT INTO emp VALUES (7654, 'MARTIN', 'SALESMAN', 7698, to_date('28-08-1981', 'DD-MM-YYYY'), 1250, 1400, 30);
INSERT INTO emp VALUES (7698, 'BLAKE', 'MANAGER', 7839, to_date('1-09-1981', 'DD-MM-YYYY'), 2850, NULL, 30);
INSERT INTO emp VALUES (7782, 'CLARK', 'MANAGER', 7839, to_date('9-10-1981', 'DD-MM-YYYY'), 2450, NULL, 10);
INSERT INTO emp VALUES (7788, 'SCOTT', 'ANALYST', 7566, to_date('09-02-1982', 'DD-MM-YYYY'), 3000, NULL, 20);
INSERT INTO emp VALUES (7839, 'KING', 'PRESIDENT', NULL, to_date('17-03-1981', 'DD-MM-YYYY'), 5000, NULL, 10);
INSERT INTO emp VALUES (7844, 'TURNER', 'SALESMAN', 7698, to_date('8-09-1981', 'DD-MM-YYYY'), 1500, 0, 30);
INSERT INTO emp VALUES (7876, 'ADAMS', 'CLERK',  7788, to_date('12-08-1983', 'DD-MM-YYYY'), 1100, NULL, 20);
INSERT INTO emp VALUES (7900, 'JAMES', 'CLERK',  7698, to_date('3-11-1981', 'DD-MM-YYYY'), 950, NULL, 30);
INSERT INTO emp VALUES (7902, 'FORD', 'ANALYST', 7566, to_date('3-12-1981', 'DD-MM-YYYY'), 3000, NULL, 20);
INSERT INTO emp VALUES (7934, 'MILLER', 'CLERK',  7782, to_date('23-10-1982', 'DD-MM-YYYY'), 1300, NULL, 10);

ALTER TABLE emp ADD CONSTRAINT emp_pk PRIMARY KEY (empno);
ALTER TABLE emp ADD CONSTRAINT emp_dept_pk FOREIGN KEY (deptno) REFERENCING DEPT (deptno);

CREATE INDEX emp_job_i ON emp (job);
CREATE INDEX emp_mgr_i ON emp (mgr);

execute dbms_stats.gather_table_stats(user, 'emp')

CREATE TABLE bonus
     (ename VARCHAR2(10),
      job VARCHAR2(9),
      sal NUMBER,
      comm NUMBER);

execute dbms_stats.gather_table_stats(user, 'bonus');

set linesize 1000
set pagesize 2000
set autotrace off
ALTER SESSION SET statistics_level = all;


SELECT *
FROM emp
WHERE NOT EXISTS (SELECT /*+ no_unnest */ 0 
               FROM dept 
               WHERE dept.dname = 'SALES' AND dept.deptno = emp.deptno)
AND NOT EXISTS (SELECT /*+ no_unnest */ 0 
             FROM bonus 
             WHERE bonus.ename = emp.ename);

SELECT * FROM table(dbms_xplan.display_cursor(null,null,'allstats last'));

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------------
SQL_ID  143p2rxtjxj0m, child number 0
-------------------------------------
SELECT * FROM emp WHERE NOT EXISTS (SELECT /*+ no_unnest */ 0
     FROM dept                WHERE dept.dname = 'SALES' AND
dept.deptno = emp.deptno) AND NOT EXISTS (SELECT /*+ no_unnest */ 0
         FROM bonus              WHERE bonus.ename = emp.ename)

Plan hash value: 2272441335

--------------------------------------------------------------------------------------------------
| Id  | Operation                    | Name    | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
--------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT             |         |      1 |        |      8 |00:00:00.01 |      14 |
|*  1 |  FILTER                      |         |      1 |        |      8 |00:00:00.01 |      14 |
|   2 |   TABLE ACCESS FULL          | EMP     |      1 |     14 |     14 |00:00:00.01 |       8 |
|*  3 |   TABLE ACCESS BY INDEX ROWID| DEPT    |      3 |      1 |      1 |00:00:00.01 |       6 |
|*  4 |    INDEX UNIQUE SCAN         | DEPT_PK |      3 |      1 |      3 |00:00:00.01 |       3 |
|*  5 |   TABLE ACCESS FULL          | BONUS   |      8 |      1 |      0 |00:00:00.01 |       0 |
--------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   1 - filter(( IS NULL AND  IS NULL))
   3 - filter("DEPT"."DNAME"='SALES')
   4 - access("DEPT"."DEPTNO"=:B1)
   5 - filter("BONUS"."ENAME"=:B1)


已选择28行。

--原因分析:
--为什么执行计划中ID=3的地方STARTS 3次,因为虽然有8条记录,但是不重复的只有3个(ACCOUNTING、RESEARCH、 SALES)

SELECT dname, count(*)
FROM emp, dept
WHERE emp.deptno = dept.deptno
GROUP BY dname;

DNAME            COUNT(*)
-------------- ----------
ACCOUNTING              3
RESEARCH                5
SALES                   6

--接下来的为什么执行计划中ID=5的地方是STARTS 8次,因为返回8条
SELECT ename
FROM emp
WHERE NOT EXISTS (SELECT /*+ no_unnest */ 0 
                    FROM dept 
                 WHERE dept.dname = 'SALES' AND dept.deptno = emp.deptno);

ENAME
----------
SMITH
JONES
CLARK
SCOTT
KING
ADAMS
FORD
MILLER

已选择8行。               
             
             

--做一些试验看看
update emp set deptno=40 where deptno=20 and rownum=1;
commit;  
         
SELECT dname, count(*)
   FROM emp, dept
   WHERE emp.deptno = dept.deptno
   GROUP BY dname;

DNAME            COUNT(*)
-------------- ----------
ACCOUNTING              3
OPERATIONS              1
RESEARCH                4
SALES                   6  

SELECT *
FROM emp
WHERE NOT EXISTS (SELECT /*+ no_unnest */ 0 
               FROM dept 
               WHERE dept.dname = 'SALES' AND dept.deptno = emp.deptno)
AND NOT EXISTS (SELECT /*+ no_unnest */ 0 
             FROM bonus 
             WHERE bonus.ename = emp.ename);
             
SELECT * FROM table(dbms_xplan.display_cursor(null,null,'allstats last'));
PLAN_TABLE_OUTPUT
-------------------------------------------------------------------------------------------------
SQL_ID  143p2rxtjxj0m, child number 0
-------------------------------------
SELECT * FROM emp WHERE NOT EXISTS (SELECT /*+ no_unnest */ 0
     FROM dept                WHERE dept.dname = 'SALES' AND
dept.deptno = emp.deptno) AND NOT EXISTS (SELECT /*+ no_unnest */ 0
         FROM bonus              WHERE bonus.ename = emp.ename)

Plan hash value: 2272441335

--------------------------------------------------------------------------------------------------
| Id  | Operation                    | Name    | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
--------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT             |         |      1 |        |      8 |00:00:00.01 |      16 |
|*  1 |  FILTER                      |         |      1 |        |      8 |00:00:00.01 |      16 |
|   2 |   TABLE ACCESS FULL          | EMP     |      1 |     14 |     14 |00:00:00.01 |       8 |
|*  3 |   TABLE ACCESS BY INDEX ROWID| DEPT    |      4 |      1 |      1 |00:00:00.01 |       8 |
|*  4 |    INDEX UNIQUE SCAN         | DEPT_PK |      4 |      1 |      4 |00:00:00.01 |       4 |
|*  5 |   TABLE ACCESS FULL          | BONUS   |      8 |      1 |      0 |00:00:00.01 |       0 |
--------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   1 - filter(( IS NULL AND  IS NULL))
   3 - filter("DEPT"."DNAME"='SALES')
   4 - access("DEPT"."DEPTNO"=:B1)
   5 - filter("BONUS"."ENAME"=:B1)


已选择28行。

--有变化!执行计划中ID=3的地方STARTS从3次变为4次!不过ID=5的地方的STARTS依然是8次。
原因很简单,如下:
ENAME
----------
SMITH
JONES
CLARK
SCOTT
KING
ADAMS
FORD
MILLER

--继续做试验,删除SMITH的记录。
delete from emp where ename='SMITH';
commit;

--然后再执行观察执行计划。
SELECT *
FROM emp
WHERE NOT EXISTS (SELECT /*+ no_unnest */ 0 
               FROM dept 
               WHERE dept.dname = 'SALES' AND dept.deptno = emp.deptno)
AND NOT EXISTS (SELECT /*+ no_unnest */ 0 
             FROM bonus 
             WHERE bonus.ename = emp.ename);
             
SELECT * FROM table(dbms_xplan.display_cursor(null,null,'allstats last'));

PLAN_TABLE_OUTPUT
---------------------------------------------------------------------------------------------------
SQL_ID  143p2rxtjxj0m, child number 0
-------------------------------------
SELECT * FROM emp WHERE NOT EXISTS (SELECT /*+ no_unnest */ 0
     FROM dept                WHERE dept.dname = 'SALES' AND
dept.deptno = emp.deptno) AND NOT EXISTS (SELECT /*+ no_unnest */ 0
         FROM bonus              WHERE bonus.ename = emp.ename)

Plan hash value: 2272441335
--------------------------------------------------------------------------------------------------
| Id  | Operation                    | Name    | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
--------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT             |         |      1 |        |      7 |00:00:00.01 |      14 |
|*  1 |  FILTER                      |         |      1 |        |      7 |00:00:00.01 |      14 |
|   2 |   TABLE ACCESS FULL          | EMP     |      1 |     14 |     13 |00:00:00.01 |       8 |
|*  3 |   TABLE ACCESS BY INDEX ROWID| DEPT    |      3 |      1 |      1 |00:00:00.01 |       6 |
|*  4 |    INDEX UNIQUE SCAN         | DEPT_PK |      3 |      1 |      3 |00:00:00.01 |       3 |
|*  5 |   TABLE ACCESS FULL          | BONUS   |      7 |      1 |      0 |00:00:00.01 |       0 |
--------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------

   1 - filter(( IS NULL AND  IS NULL))
   3 - filter("DEPT"."DNAME"='SALES')
   4 - access("DEPT"."DEPTNO"=:B1)
   5 - filter("BONUS"."ENAME"=:B1)


已选择28行。

--这下ID=5处的STARTS从原来的8变成7了。

  联合型(相关联)---update

/*
  我们把有多个孩子的操作定义为联合型操作,其中一个孩子控制其他孩子的操作我们定义为关联联合型操作
*/

DROP TABLE bonus cascade constraints PURGE;
DROP TABLE emp cascade constraints PURGE;
DROP TABLE dept cascade constraints PURGE;

CREATE TABLE dept
    (deptno NUMBER(2),
     dname VARCHAR2(14),
     loc VARCHAR2(13) );

INSERT INTO dept VALUES (10, 'ACCOUNTING', 'NEW YORK');
INSERT INTO dept VALUES (20, 'RESEARCH', 'DALLAS');
INSERT INTO dept VALUES (30, 'SALES',   'CHICAGO');
INSERT INTO dept VALUES (40, 'OPERATIONS', 'BOSTON');

ALTER TABLE dept ADD CONSTRAINT dept_pk PRIMARY KEY (deptno);

execute dbms_stats.gather_table_stats(user, 'dept')

CREATE TABLE emp
    (empno NUMBER(4) NOT NULL,
     ename VARCHAR2(10),
     job VARCHAR2(9),
     mgr NUMBER(4),
     hiredate DATE,
     sal NUMBER(7, 2),
     comm NUMBER(7, 2),
     deptno NUMBER(2));

INSERT INTO emp VALUES (7369, 'SMITH', 'CLERK', 7902, to_date('17-04-1980', 'DD-MM-YYYY'), 800, NULL, 20);
INSERT INTO emp VALUES (7499, 'ALLEN', 'SALESMAN', 7698, to_date('20-05-1981', 'DD-MM-YYYY'), 1600, 300, 30);
INSERT INTO emp VALUES (7521, 'WARD', 'SALESMAN', 7698, to_date('22-06-1981', 'DD-MM-YYYY'), 1250, 500, 30);
INSERT INTO emp VALUES (7566, 'JONES', 'MANAGER', 7839, to_date('2-07-1981', 'DD-MM-YYYY'), 2975, NULL, 20);
INSERT INTO emp VALUES (7654, 'MARTIN', 'SALESMAN', 7698, to_date('28-08-1981', 'DD-MM-YYYY'), 1250, 1400, 30);
INSERT INTO emp VALUES (7698, 'BLAKE', 'MANAGER', 7839, to_date('1-09-1981', 'DD-MM-YYYY'), 2850, NULL, 30);
INSERT INTO emp VALUES (7782, 'CLARK', 'MANAGER', 7839, to_date('9-10-1981', 'DD-MM-YYYY'), 2450, NULL, 10);
INSERT INTO emp VALUES (7788, 'SCOTT', 'ANALYST', 7566, to_date('09-02-1982', 'DD-MM-YYYY'), 3000, NULL, 20);
INSERT INTO emp VALUES (7839, 'KING', 'PRESIDENT', NULL, to_date('17-03-1981', 'DD-MM-YYYY'), 5000, NULL, 10);
INSERT INTO emp VALUES (7844, 'TURNER', 'SALESMAN', 7698, to_date('8-09-1981', 'DD-MM-YYYY'), 1500, 0, 30);
INSERT INTO emp VALUES (7876, 'ADAMS', 'CLERK',  7788, to_date('12-08-1983', 'DD-MM-YYYY'), 1100, NULL, 20);
INSERT INTO emp VALUES (7900, 'JAMES', 'CLERK',  7698, to_date('3-11-1981', 'DD-MM-YYYY'), 950, NULL, 30);
INSERT INTO emp VALUES (7902, 'FORD', 'ANALYST', 7566, to_date('3-12-1981', 'DD-MM-YYYY'), 3000, NULL, 20);
INSERT INTO emp VALUES (7934, 'MILLER', 'CLERK',  7782, to_date('23-10-1982', 'DD-MM-YYYY'), 1300, NULL, 10);

ALTER TABLE emp ADD CONSTRAINT emp_pk PRIMARY KEY (empno);
ALTER TABLE emp ADD CONSTRAINT emp_dept_pk FOREIGN KEY (deptno) REFERENCING DEPT (deptno);

CREATE INDEX emp_job_i ON emp (job);
CREATE INDEX emp_mgr_i ON emp (mgr);

execute dbms_stats.gather_table_stats(user, 'emp')

CREATE TABLE bonus
     (ename VARCHAR2(10),
      job VARCHAR2(9),
      sal NUMBER,
      comm NUMBER);

execute dbms_stats.gather_table_stats(user, 'bonus');

set linesize 1000
set pagesize 2000
set autotrace off

ALTER SESSION SET statistics_level = all;


REM Operation UPDATE

UPDATE emp e1
SET sal = (SELECT avg(sal) FROM emp e2 WHERE e2.deptno = e1.deptno),
 comm = (SELECT avg(comm) FROM emp e3);

SELECT * FROM table(dbms_xplan.display_cursor(null,null,'allstats last'));
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------
SQL_ID  aj9bzs3ptc6wn, child number 0
-------------------------------------
UPDATE emp e1 SET sal = (SELECT avg(sal) FROM emp e2 WHERE e2.deptno =
e1.deptno),  comm = (SELECT avg(comm) FROM emp e3)

Plan hash value: 1690508028
--------------------------------------------------------------------------------------
| Id  | Operation           | Name | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
--------------------------------------------------------------------------------------
|   0 | UPDATE STATEMENT    |      |      1 |        |      0 |00:00:00.01 |      65 |
|   1 |  UPDATE             | EMP  |      1 |        |      0 |00:00:00.01 |      65 |
|   2 |   TABLE ACCESS FULL | EMP  |      1 |     14 |     14 |00:00:00.01 |       7 |
|   3 |   SORT AGGREGATE    |      |      3 |      1 |      3 |00:00:00.01 |      21 |
|*  4 |    TABLE ACCESS FULL| EMP  |      3 |      5 |     14 |00:00:00.01 |      21 |
|   5 |   SORT AGGREGATE    |      |      1 |      1 |      1 |00:00:00.01 |       7 |
|   6 |    TABLE ACCESS FULL| EMP  |      1 |     14 |     14 |00:00:00.01 |       7 |
--------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   4 - filter("E2"."DEPTNO"=:B1)


已选择24行。

rollback;

  联合型(相关联)04_树形

/*
 相关联联合型中的CONNECT BY WITH FLITERING操作吧。
*/

DROP TABLE bonus cascade constraints PURGE;
DROP TABLE emp cascade constraints PURGE;
DROP TABLE dept cascade constraints PURGE;

CREATE TABLE dept
    (deptno NUMBER(2),
     dname VARCHAR2(14),
     loc VARCHAR2(13) );

INSERT INTO dept VALUES (10, 'ACCOUNTING', 'NEW YORK');
INSERT INTO dept VALUES (20, 'RESEARCH', 'DALLAS');
INSERT INTO dept VALUES (30, 'SALES',   'CHICAGO');
INSERT INTO dept VALUES (40, 'OPERATIONS', 'BOSTON');

ALTER TABLE dept ADD CONSTRAINT dept_pk PRIMARY KEY (deptno);

execute dbms_stats.gather_table_stats(user, 'dept')

CREATE TABLE emp
    (empno NUMBER(4) NOT NULL,
     ename VARCHAR2(10),
     job VARCHAR2(9),
     mgr NUMBER(4),
     hiredate DATE,
     sal NUMBER(7, 2),
     comm NUMBER(7, 2),
     deptno NUMBER(2));

INSERT INTO emp VALUES (7369, 'SMITH', 'CLERK',    7902, to_date('17-04-1980','DD-MM-YYYY'), 800,  NULL,20);
INSERT INTO emp VALUES (7499, 'ALLEN', 'SALESMAN', 7698, to_date('20-05-1981','DD-MM-YYYY'), 1600, 300, 30);
INSERT INTO emp VALUES (7521, 'WARD',  'SALESMAN', 7698, to_date('22-06-1981','DD-MM-YYYY'), 1250, 500, 30);
INSERT INTO emp VALUES (7566, 'JONES', 'MANAGER',  7839, to_date('2-07-1981', 'DD-MM-YYYY'), 2975, NULL,20);
INSERT INTO emp VALUES (7654, 'MARTIN','SALESMAN', 7698, to_date('28-08-1981','DD-MM-YYYY'), 1250, 1400,30);
INSERT INTO emp VALUES (7698, 'BLAKE', 'MANAGER',  7839, to_date('1-09-1981', 'DD-MM-YYYY'), 2850, NULL,30);
INSERT INTO emp VALUES (7782, 'CLARK', 'MANAGER',  7839, to_date('9-10-1981', 'DD-MM-YYYY'), 2450, NULL,10);
INSERT INTO emp VALUES (7788, 'SCOTT', 'ANALYST',  7566, to_date('09-02-1982','DD-MM-YYYY'), 3000, NULL,20);
INSERT INTO emp VALUES (7839, 'KING',  'PRESIDENT', NULL,to_date('17-03-1981','DD-MM-YYYY'), 5000, NULL,10);
INSERT INTO emp VALUES (7844, 'TURNER','SALESMAN', 7698, to_date('8-09-1981', 'DD-MM-YYYY'), 1500, 0, 30);
INSERT INTO emp VALUES (7876, 'ADAMS', 'CLERK',    7788, to_date('12-08-1983','DD-MM-YYYY'), 1100, NULL,20);
INSERT INTO emp VALUES (7900, 'JAMES', 'CLERK',    7698, to_date('3-11-1981', 'DD-MM-YYYY'), 950,  NULL,30);
INSERT INTO emp VALUES (7902, 'FORD',  'ANALYST',  7566, to_date('3-12-1981', 'DD-MM-YYYY'), 3000, NULL,20);
INSERT INTO emp VALUES (7934, 'MILLER','CLERK',    7782, to_date('23-10-1982','DD-MM-YYYY'), 1300, NULL,10);

ALTER TABLE emp ADD CONSTRAINT emp_pk PRIMARY KEY (empno);
ALTER TABLE emp ADD CONSTRAINT emp_dept_pk FOREIGN KEY (deptno) REFERENCING DEPT (deptno);

CREATE INDEX emp_job_i ON emp (job);
CREATE INDEX emp_mgr_i ON emp (mgr);

execute dbms_stats.gather_table_stats(user, 'emp')

CREATE TABLE bonus
     (ename VARCHAR2(10),
      job VARCHAR2(9),
      sal NUMBER,
      comm NUMBER);

execute dbms_stats.gather_table_stats(user, 'bonus');


set linesize 1000
set pagesize 2000
set autotrace off
ALTER SESSION SET statistics_level = all;


PAUSE

REM Operation CONNECT BY WITH FILTERING

COLUMN ename FORMAT A10
COLUMN manager FORMAT A10

SELECT /*+ connect_by_filtering  */ level, rpad('-',level-1,'-')||ename AS ename, prior ename AS manager
FROM emp
START WITH mgr IS NULL
CONNECT BY PRIOR empno = mgr;

SELECT * FROM table(dbms_xplan.display_cursor(null,null,'allstats last'));

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------------------------------------------
SQL_ID  62wv394wc9zqa, child number 0
-------------------------------------
SELECT /*+ connect_by_filtering  */ level, rpad('-',level-1,'-')||ename
AS ename, prior ename AS manager FROM emp START WITH mgr IS NULL
CONNECT BY PRIOR empno = mgr

Plan hash value: 1519159851
--------------------------------------------------------------------------------------------------------------------------------
| Id  | Operation                     | Name      | Starts | E-Rows | A-Rows |   A-Time   | Buffers |  OMem |  1Mem | Used-Mem |
--------------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT              |           |      1 |        |     14 |00:00:00.01 |      15 |       |       |          |
|*  1 |  CONNECT BY WITH FILTERING    |           |      1 |        |     14 |00:00:00.01 |      15 |  2048 |  2048 | 2048  (0)|
|*  2 |   TABLE ACCESS FULL           | EMP       |      1 |      1 |      1 |00:00:00.01 |       7 |       |       |          |
|   3 |   NESTED LOOPS                |           |      4 |      2 |     13 |00:00:00.01 |       8 |       |       |          |
|   4 |    CONNECT BY PUMP            |           |      4 |        |     14 |00:00:00.01 |       0 |       |       |          |
|   5 |    TABLE ACCESS BY INDEX ROWID| EMP       |     14 |      2 |     13 |00:00:00.01 |       8 |       |       |          |
|*  6 |     INDEX RANGE SCAN          | EMP_MGR_I |     14 |      2 |     13 |00:00:00.01 |       5 |       |       |          |
--------------------------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   1 - access("MGR"=PRIOR NULL)
   2 - filter("MGR" IS NULL)
   6 - access("connect$_by$_pump$_002"."PRIOR empno "="MGR")
       filter("MGR" IS NOT NULL)

已选择28行。

原理分析:

 SELECT /*+ connect_by_filtering */ level, rpad('-',level-1,'-')||ename AS ename, prior ename AS manager
    FROM emp
    START WITH mgr IS NULL
    CONNECT BY PRIOR empno = mgr;

     LEVEL ENAME      MANAGER
---------- ---------- ----------
         1 KING
         2 -JONES     KING
         3 --SCOTT    JONES
         4 ---ADAMS   SCOTT
         3 --FORD     JONES
         4 ---SMITH   FORD
         2 -BLAKE     KING
         3 --ALLEN    BLAKE
         3 --WARD     BLAKE
         3 --MARTIN   BLAKE
         3 --TURNER   BLAKE
         3 --JAMES    BLAKE
         2 -CLARK     KING
         3 --MILLER   CLARK

已选择14行。

/*
--为什么执行计划中ID=4的地方STARTS 4次,因为完成4次执行
第1次得到KING 
第2次得到 JONES、BLAKE、CLARK
第3次得到 SCOTT、 FORD、 ALLEN、 WARD、 MARTIN、 TURNER、 JAMES、 MILLER
第4次得到 ADAMS、 SMITH
为什么执行计划中ID=6的部分是执行14次,因为返回14条。
*/


SELECT /*+ no_connect_by_filtering full(emp) */ level, emp.*
FROM emp
START WITH mgr = 7839
CONNECT BY PRIOR empno = mgr;

SELECT * FROM table(dbms_xplan.display_cursor(null,null,'allstats last'));

  

posted @ 2020-03-25 12:51  石shi  阅读(315)  评论(0编辑  收藏  举报