使用EXPLAIN PLAN获取SQL语句…

使用EXPLAIN PLAN获取SQL语句执行计划

转自:http://blog.csdn.net/leshami/article/details/6837771

SQL语句的性能从一定程度上影响整个数据库的性能。很多情况下,数据库性能的低下差不多都是不良SQL语句所引起。
而SQL语句的执行计划则决定了SQL语句将会采用何种方式从数据库提取数据并返回给客户端,
本文描述的将是如何通过EXPLAIN PLAN来获取SQL语句的执行计划。

一、获取SQL语句执行计划的方式
        1. 使用explain plan将执行计划加载到表plan_table,然后查询该表来获取预估的执行计划
        2. 查询动态性能视图v$sql_plan,v$sql_plan_statistics,v$sql_workarea等来获取已缓存到库缓存中的真实执行计划
        3. 查询自动工作量资料库(Automatic Workload Repository)或查询Statspack,即从资料库中获取执行计划
        4. 启用执行计划跟踪功能,即autotrace功能
        5. 使用PL/SQL Developer提供的获取执行计划方法
        6. 使用Toad工具来获取执行计划

下面主要讨论使用explain plan获取执行计划的方法

二、explain plan工作实质、前提及操作方法
        1. 工作实质
          将SQL语句预估的执行计划加载到表plan_table,是对表plan_table 执行了DML操作,故不会执行隐式提交
          可以对select,insert,update,merge,delete,create table, create index,alter index等加载执行计划到plan_table

        2. 前提条件
          需要先创建plan_table,创建方法:@?/rdbms/admin/utlxplan
          对当前的SQL语句有执行权限
        对所依赖的对象有相应操作的权限

        3. 使用方法:
            explain plan for select * from scott.emp where ename='SCOTT';      --未设置标记位
            explain plan set statement_id='TEST' for select * from scott.emp where ename='SCOTT'  --设置标记位为TEST

三、实战演习

1.环境  

scott@ORCL> select * from v$version;                                                         
                                                                                                                                 
BANNER                                                                                                                     
----------------------------------------------------------------   
Oracle Database 10g Enterprise Edition Release 10.2.0.1.0 - Prod  

2.创建测试表演示获取执行计划

scott@ORCL> create table t as select * from all_objects where rownum<=1000;                                                                           
                                                                                                                                                                                               
Table created.                                                                                                                                                                                                     
                                                                                                                                                                                                                                 
--加载创建表的执行计划(DDL 执行计划)                                                                                                                                                           
scott@ORCL> explain plan set statement_id='T1' for create table t1 as select * from t;                                                     
                                                                                                                                                                                                                                 
Explained.                                                                                                                                                                                                             
                                                                                                                                                                                                                                 
--使用下面的语句从plan_table 获取执行计划                                                                                                                                                 
col OPERATION format a25                                                                                                                                                                                 
col OPTIONS format a25                                                                                                                                                                                     
col OBJECT_NAME format a25                                                                                                                                                                             
SELECT lpad(' ', 2 * (LEVEL - 1)) || operation operation                                                                                                                 
      ,options                                                                                                                                                                                                         
      ,object_name                                                                                                                                                                                                 
      ,position pos                                                                                                                                                                                               
      ,bytes                                                                                                                                                                                                             
      ,cost                                                                                                                                                                                                               
FROM plan_table                                                                                                                                                                                                   
START WITH id = 0                                                                                                                                                                                               
AND statement_id =upper( '&input_statement_id')                                                                                                                                   
CONNECT BY PRIOR id = parent_id;                                                                                                                                                                 
                                                                                                                                                                                                                                 
Enter value for input_statement_id: T1                                                                                                                                                     
old    9: AND statement_id =upper( '&input_statement_id')                                                                                                                 
new    9: AND statement_id =upper( 'T1')                                                                                                                                                   
                                                                                                                                                                                                                                 
OPERATION                                OPTIONS                                    OBJECT_NAME                                          POS          BYTES            COST     
------------------------- ------------------------- ------------------------- ---------- ---------- ----------       
CREATE TABLE STATEMENT                                                                                                                                8          79000                  8     
  LOAD AS SELECT                                                                      T1                                                                1                                                 
      TABLE ACCESS                  FULL                                          T                                                                  1          79000                  5     
     

     
--创建测试表t1并收集统计信息                                                                                                                                                                           
scott@ORCL> create table t1 nologging as select * from t;                                                                                                               
                                                                                                                                                                                                                                 
scott@ORCL> exec dbms_stats.gather_table_stats('SCOTT','T1');                                                                                                       
                                                                                                                                                                                                                                 
--使用explain plan加载创建索引的执行计划                                                                                                                                                   
scott@ORCL> explain plan set statement_id='IDX' for create index i_t1 on t1(object_id);                                                   
                                                                                                                                                                                                                                 
Explained.                                                                                                                                                                                                             
                                                                                                                                                                                                                                 
scott@ORCL> @Get_Plan                                                                                                                                                                                       
Enter value for input_statement_id: IDX                                                                                                                                                   
old    9: AND statement_id =upper( '&input_statement_id')                                                                                                                 
new    9: AND statement_id =upper( 'IDX')                                                                                                                                                 
                                                                                                                                                                                                                                 
OPERATION                                OPTIONS                                    OBJECT_NAME                                          POS          BYTES            COST     
------------------------- ------------------------- ------------------------- ---------- ---------- ----------       
CREATE INDEX STATEMENT                                                                                                                                6            4000                  6     
  INDEX BUILD                        NON UNIQUE                              I_T1                                                            1                                                 
      SORT                                  CREATE INDEX                                                                                                1            4000                           
          TABLE ACCESS              FULL                                          T1                                                                1            4000                  5     
                                                                                                                                                                                                                                 
scott@ORCL> CREATE INDEX i_t1 ON t1 (object_id);                                                                                                                                 
                                                                                                                                                                                                                                 
scott@ORCL> delete from plan_table;                     



3.使用自顶向下的读取方法获取执行计划

--使用explain plan加载重建索引的执行计划                                                                                                                                                       
scott@ORCL> explain plan set statement_id='A_IDX' for alter index i_t1 rebuild;                                                                       
                                                                                                                                                                                                                                     
Explained.                                                                                                                                                                                                                 
                                                                                                                                                                                                                                     
--执行下面的语句来获的A_IDX的执行计划,其结果是从上至下来读,从最内侧往最外侧读。                                                                     
SELECT LPAD(' ', 2 * (LEVEL - 1)) || LEVEL || '.' || NVL(POSITION, 0) || ' ' ||                                                                       
      OPERATION || ' ' || OPTIONS || ' ' || OBJECT_NAME || ' ' ||                                                                                                       
      OBJECT_TYPE || ' ' ||                                                                                                                                                                                   
      DECODE(ID, 0, STATEMENT_ID || ' Cost = ' || POSITION) || COST || ' ' ||                                                                               
      OBJECT_NODE "Query Plan"                                                                                                                                                                             
FROM PLAN_TABLE                                                                                                                                                                                                       
START WITH ID = 0                                                                                                                                                                                                   
AND STATEMENT_ID = UPPER('&input_statement_id')                                                                                                                                       
CONNECT BY PRIOR ID = PARENT_ID                                                                                                                                                                       
AND STATEMENT_ID = UPPER('&input_statement_id');                                                                                                                                     
                                                                                                                                                                                                                                     
Enter value for input_statement_id: A_IDX                                                                                                                                                   
old    8: AND STATEMENT_ID = UPPER('&input_statement_id')                                                                                                                     
new    8: AND STATEMENT_ID = UPPER('A_IDX')                                                                                                                                                 
Enter value for input_statement_id: A_IDX                                                                                                                                                   
old  10: AND STATEMENT_ID = UPPER('&input_statement_id')                                                                                                                     
new  10: AND STATEMENT_ID = UPPER('A_IDX')                                                                                                                                                 
                                                                                                                                                                                                                                     
Query Plan                                                                                                                                                                                                                 
---------------------------------------------------------------------------------------------                                             
1.2 ALTER INDEX STATEMENT      A_IDX Cost = 22                                                                                                                                             
  2.1 INDEX BUILD NON UNIQUE I_T1                                                                                                                                                                   
      3.1 SORT CREATE INDEX                                                                                                                                                                                   
          4.1 INDEX FAST FULL SCAN I_T1 INDEX 2                                                                                                                                               
                                                                                                                                                                                                                                     

--使用explain plan加载查询语句的执行计划                                                                                                                                                       
scott@ORCL> explain plan set statement_id='QUERY' for                                                                                                                           
  2  select ename,dname                                                                                                                                                                                       
  3  from emp join dept                                                                                                                                                                                       
  4  on emp.deptno=dept.deptno                                                                                                                                                                         
  5  where dept.deptno=30;                                                                                                                                                                                 
                                                                                                                                                                                                                                     
Explained.                                                                                                                                                                                                                 
                                                                                                                                                                                                                                     
scott@ORCL> @Get_Plan2                                                                                                                                                                                         
                                                                                                                                                                                                                                     
Query Plan                                                                                                                                                                                                                 
--------------------------------------------------------------------------------------------                                               
1.4 SELECT STATEMENT      QUERY Cost = 44                                                                                                                                                       
  2.1 NESTED LOOPS      4                                                                                                                                                                                       
      3.1 TABLE ACCESS BY INDEX ROWID DEPT TABLE 1                                                                                                                                     
          4.1 INDEX UNIQUE SCAN PK_DEPT INDEX (UNIQUE) 0                                                                                                                             
      3.2 TABLE ACCESS FULL EMP TABLE 3                                                                                                                                                           

  上面的例子的读取方法:
            执行4.1的索引唯一扫描
            将4.1的结果集返回给3.1
            执行3.2的全表扫描
            将3.1和3.2步骤的结果集返回给2.1
            执行2.1的嵌套循环
            返回最终结果集
            注意嵌套循环的查询方法
            Oracle 从第一个行源中读取第一行,然后和第二个行源中的所有记录行进行比对,所有匹配的记录放在结果集中,然后Oracle 将读第一
            个行源中的下一行。依次类推,直到第一行源中的所有行处理完毕。

         
4.使用构建树方式查看执行计划

scott@ORCL> delete from plan_table;                                                                                                                                                     
                                                                                                                                                                                                                           
--使用explian plan加载SQL查询执行计划                                                                                                                                                   
scott@ORCL> explain plan set statement_id='QUERY2' for                                                                                                               
  2  select ename,dname                                                                                                                                                                             
  3  from emp join dept                                                                                                                                                                             
  4  on emp.deptno=dept.deptno                                                                                                                                                               
  5  where emp.empno=7788;                                                                                                                                                                       
                                                                                                                                                                                                                           
Explained.                                                                                                                                                                                                       
                                                                                                                                                                                                                           
--使用下面的SQl查询来生成构建树                                                                                                                                                               
col operation format a30                                                                                                                                                                           
col options format a20                                                                                                                                                                               
col "OBJECT NAME" format a25                                                                                                                                                                   
col order format a10                                                                                                                                                                                   
col opt format a15                                                                                                                                                                                       
SELECT LPAD(' ', 2 * (LEVEL - 1)) || operation "OPERATION",                                                                                                     
      options "OPTIONS",                                                                                                                                                                               
      DECODE(TO_CHAR(id),                                                                                                                                                                             
              '0',                                                                                                                                                                                                   
              'COST = ' || NVL(TO_CHAR(position), 'n/a'),                                                                                                                     
              object_name) "OBJECT NAME",                                                                                                                                                     
      id || '-' || NVL(parent_id, 0) || '-' || NVL(position, 0) "ORDER",                                                                               
      SUBSTR(optimizer, 1, 6) "OPT"                                                                                                                                                         
FROM plan_table                                                                                                                                                                                             
START WITH id = 0                                                                                                                                                                                         
AND statement_id = UPPER('&input_statement_id')                                                                                                                             
CONNECT BY PRIOR id = parent_id                                                                                                                                                             
AND statement_id = UPPER('&input_statement_id');                                                                                                                           
                                                                                                                                                                                                                           
OPERATION                                          OPTIONS                          OBJECT NAME                            ORDER          OPT                                   
------------------------------ -------------------- ------------------------- ---------- ---------------             
SELECT STATEMENT                                                                      COST = 2                                  0-0-2          ALL_RO                             
  NESTED LOOPS                                                                                                                              1-0-1                                                     
      TABLE ACCESS                            BY INDEX ROWID            EMP                                            2-1-1          ANALYZ                             
          INDEX                                      UNIQUE SCAN                  PK_EMP                                      3-2-1          ANALYZ                             
      TABLE ACCESS                            BY INDEX ROWID            DEPT                                          4-1-2          ANALYZ                             
          INDEX                                      UNIQUE SCAN                  PK_DEPT                                    5-4-1          ANALYZ                                     

          查询结果中的order列与opt列
          order
                    order列的指名了ID,父ID,以及执行计划中这一步骤的位置。
                    ID列标识了这个步骤,但并没有说明执行的顺序
                    父ID表明了这个步骤中的父步骤
                    位置信息说明了父ID相同的子操作的执行顺序       
          opt
                    说明当前优化器使用的模式
          分析
                    首先会从步骤3开始执行,步骤3通过索引唯一扫描PK_EMP将得到的结果集返回给父步骤2
                    步骤2根据上一子步骤3得到的rowid访问表EMP并将结果集返回给父步骤1
                    对于步骤2检索到的每一行数据,步骤1会将deptno传递给步骤5
                    步骤5根据得到的deptno执行索引唯一扫描并将结果集返回给步骤4
                    步骤4根据步骤5得到的rowid 访问表dept,并将结果集返回给父步骤1
                    对于步骤3中剩余的行依次按上述方式将所有结果集返回给步骤1
                    步骤1将获得的最终结果集返回给步骤0,SQL完成查询
 
          根据查询返回的结果来构建执行计划树
                    从ID为1的列开始,作为根节点
                    寻找所有父ID为1的所有子ID,如本例为2和4,将其纳入树中
                    分别寻找以2和4为父ID的所有子ID,将其纳入树中
                    如此循环直到所有的ID没有父ID

                          ---------------
                          NESTED LOOP (1)
                          ---------------
                            -              -
                        -                    -   
                      -                          -
            ---------              ----------
            EMP (2)                    DEPT(4)
            ---------              ----------
                    -                              -
                  -                                  -
    ---------                          ----------
    PK_EMP(3)                          PK_DEPT(5)       
    ---------                        ----------
   
   
5.通过Oracle自带的SQL语句执行计划

  可以通过Oracle提供的SQl语句来获得当前会话最后一条SQL语句的执行计划

    utlxpls.sql    -->用于查看串行执行计划
    utlxplp.sql    -->用于查看并行执行计划


scott@ORCL> @?/rdbms/admin/utlxpls.sql      --获得当前session plan_table 最后一条SQL语句的执行计划                                             
                                                                                                                                                                                                                                           
PLAN_TABLE_OUTPUT                                                                                                                                                                                                         
--------------------------------------------------------------------------------------------------                                         
Plan hash value: 2385808155                                                                                                                                                                                     
                                                                                                                                                                                                                                           
----------------------------------------------------------------------------------------                                                             
| Id  | Operation                                      | Name      | Rows  | Bytes | Cost (%CPU)| Time        |                                                           
----------------------------------------------------------------------------------------                                                             
|    0 | SELECT STATEMENT                        |                |        1 |      28 |        2    (0)| 00:00:01 |                                                           
|    1 |  NESTED LOOPS                              |                |        1 |      28 |        2    (0)| 00:00:01 |                                                           
|*  2 |    TABLE ACCESS BY INDEX ROWID| EMP        |        1 |      14 |        1    (0)| 00:00:01 |                                                           
|*  3 |      INDEX UNIQUE SCAN                | PK_EMP  |        1 |            |        0    (0)| 00:00:01 |                                                           
|    4 |    TABLE ACCESS BY INDEX ROWID| DEPT      |        4 |      56 |        1    (0)| 00:00:01 |                                                           
|*  5 |      INDEX UNIQUE SCAN                | PK_DEPT |        1 |            |        0    (0)| 00:00:01 |                                                           
----------------------------------------------------------------------------------------                                                             
                                                                                                                                                                                                                                           
Predicate Information (identified by operation id):                                                                                                                                     
---------------------------------------------------                                                                                                                                       
                                                                                                                                                                                                                                           
    2 - filter("EMP"."DEPTNO" IS NOT NULL)                                                                                                                                                         
    3 - access("EMP"."EMPNO"=7788)                                                                                                                                                                         
    5 - access("EMP"."DEPTNO"="DEPT"."DEPTNO")                                                                                                                                                 
                                                                                                                                                                                                                                           
19 rows selected.                                                                                                                                                                                                             
                                                                                                                                                                                                                                           
--加载并行SQL的执行计划                                                                                                                                                                                               
scott@ORCL> explain plan for select * from t4;                                                                                                   
                                                                                                                                                                                                                                           
Explained.                                                                                                                                                                                                                       
                                                                                                                                                                                                                                           
scott@ORCL> @?/rdbms/admin/utlxplp.sql                                                                                                                                                               
                                                                                                                                                                                                                                           
PLAN_TABLE_OUTPUT                                                                                                                                                                                                         
----------------------------------------------------------------------------------------------------------------             
Plan hash value: 128826497                                                                                                                                                                                       
                                                                                                                                                                                                                                           
--------------------------------------------------------------------------------------------------------------                 
| Id  | Operation                      | Name        | Rows  | Bytes | Cost (%CPU)| Time        |      TQ  |IN-OUT| PQ Distrib |               
--------------------------------------------------------------------------------------------------------------                 
|    0 | SELECT STATEMENT        |                  |    400K|  7817K|    183    (4)| 00:00:03 |              |          |                      |               
|    1 |  PX COORDINATOR          |                  |            |            |                      |                  |              |          |                      |               
|    2 |    PX SEND QC (RANDOM)| :TQ10000 |    400K|  7817K|    183    (4)| 00:00:03 |  Q1,00 | P->S | QC (RAND)  |               
|    3 |      PX BLOCK ITERATOR |                  |    400K|  7817K|    183    (4)| 00:00:03 |  Q1,00 | PCWC |                      |               
|    4 |        TABLE ACCESS FULL| T4            |    400K|  7817K|    183    (4)| 00:00:03 |  Q1,00 | PCWP |                      |               
--------------------------------------------------------------------------------------------------------------               
       

四、总结:
        1. explain plan并不执行当前的SQL语句,而是根据数据字典中记录的统计信息获取最佳的执行计划并加载到表plan_table。
        2. 由于统计信息,执行环境的变化,explain plan与实际的执行计划可能会有差异。
        3. 对于运行时将较长的SQL语句,不需要等到结果输出即可提前获得该SQL的执行计划,对于生产环境调试情况会减轻数据库负荷。
        4. 注意set statement_id标识符区分大小写。

你可能感兴趣的:(PL/SQL)