专题第一篇《Oracle之SQL优化专题01-查看SQL执行计划的方法》讲到了查看SQL执行计划的方法,并介绍了各种方法的应用场景,那么这一篇就主要介绍下如何看懂SQL的执行计划。毕竟如果SQL的执行计划都看不懂,那优化就无从谈起了。

关于如何看懂SQL的执行计划,我把它简单分为3个部分:

1.判断执行计划的执行顺序

**口诀:**先子后父,先上后下。
一般简单的执行计划可以直接根据这个口诀来判断执行计划的执行顺序。类似的口诀还有"最右最上"之类,其表达的意思都是一样的,选择一种自己易接受的口诀记忆即可。
举一个简单的例子:

SQL> show user
USER is "SYS"
SQL> alter session set current_schema=scott;
SQL> alter session set statistics_level = ALL;
SQL> select a.empno, a.ename, b.dname, a.job, a.sal from emp a, dept b
2 where a.deptno = b.deptno and empno = 7788
3 ; EMPNO ENAME DNAME JOB SAL
---------- ---------- -------------- --------- ----------
7788 SCOTT RESEARCH ANALYST 3000 SQL> select * from table(dbms_xplan.display_cursor(null,null,'allstats last')); PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
SQL_ID 19ktq5t44xsyp, child number 0
-------------------------------------
select a.empno, a.ename, b.dname, a.job, a.sal from emp a, dept b where
a.deptno = b.deptno and empno = 7788 Plan hash value: 2385808155 --------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers |
--------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 1 |00:00:00.01 | 4 |
| 1 | NESTED LOOPS | | 1 | 1 | 1 |00:00:00.01 | 4 |
| 2 | TABLE ACCESS BY INDEX ROWID| EMP | 1 | 1 | 1 |00:00:00.01 | 2 |
|* 3 | INDEX UNIQUE SCAN | PK_EMP | 1 | 1 | 1 |00:00:00.01 | 1 |
| 4 | TABLE ACCESS BY INDEX ROWID| DEPT | 1 | 1 | 1 |00:00:00.01 | 2 |
|* 5 | INDEX UNIQUE SCAN | PK_DEPT | 1 | 1 | 1 |00:00:00.01 | 1 |
-------------------------------------------------------------------------------------------------- Predicate Information (identified by operation id):
--------------------------------------------------- 3 - access("EMPNO"=7788)
5 - access("A"."DEPTNO"="B"."DEPTNO") 24 rows selected.

执行计划的执行顺序(按Id区分)就是 3 -> 2 -> 5 -> 4 -> 1 -> 0.

这里需要特别注意的是:

1)实际上这个所谓的口诀得到的执行顺序只是为了方便我们理解操作数据的顺序,而执行计划其实是按照Id从上往下递归调用的,简单说其实优化器首先判断是一条select语句,涉及多表关联,关联方式采用了NL Join,然后这个NL Join包含分别对EMP和DEPT索引回表的访问。

2)理解了第一点,那对于SQL语句中select部分含有标量子查询的部分不遵循这个口诀就更好理解了(因为实际这个标量子查询是最后执行的)。

举一个简单例子说明这类标量子查询:

SQL> select a.empno, a.ename, b.dname, a.job, a.sal, (select * from dual) x from emp a, dept b
2 where a.deptno = b.deptno and empno = 7788
3 ; EMPNO ENAME DNAME JOB SAL X
---------- ---------- -------------- --------- ---------- -
7788 SCOTT RESEARCH ANALYST 3000 X SQL> select * from table(dbms_xplan.display_cursor(null,null,'allstats last')); PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
SQL_ID dpbagkc0c6ctv, child number 0
-------------------------------------
select a.empno, a.ename, b.dname, a.job, a.sal, (select * from dual) x
from emp a, dept b where a.deptno = b.deptno and empno = 7788 Plan hash value: 1121436124 --------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers |
--------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 1 |00:00:00.01 | 4 |
| 1 | TABLE ACCESS FULL | DUAL | 1 | 1 | 1 |00:00:00.01 | 2 |
| 2 | NESTED LOOPS | | 1 | 1 | 1 |00:00:00.01 | 4 |
| 3 | TABLE ACCESS BY INDEX ROWID| EMP | 1 | 1 | 1 |00:00:00.01 | 2 |
|* 4 | INDEX UNIQUE SCAN | PK_EMP | 1 | 1 | 1 |00:00:00.01 | 1 |
| 5 | TABLE ACCESS BY INDEX ROWID| DEPT | 1 | 1 | 1 |00:00:00.01 | 2 |
|* 6 | INDEX UNIQUE SCAN | PK_DEPT | 1 | 1 | 1 |00:00:00.01 | 1 |
-------------------------------------------------------------------------------------------------- Predicate Information (identified by operation id):
--------------------------------------------------- 4 - access("EMPNO"=7788)
6 - access("A"."DEPTNO"="B"."DEPTNO") 25 rows selected.

执行计划的执行顺序(按Id区分),如果按口诀来看就是 1 -> 4 -> 3 -> 6 -> 5 -> 2 -> 0.

而由于这其中1是标量子查询,实际要最后执行,所以真实的执行顺序为 4 -> 3 -> 6 -> 5 -> 2 -> 1 -> 0.

2.理解执行计划每步的含义

以上面的执行计划为例,要清楚知道每一列代表的含义:
- Id
操作id号;

  • Operation

    这一列,可以看到SQL的每一步操作,深入理解就需要清楚表的主要访问方式(TABLE ACCESS FULL、TABLE ACCESS BY INDEX ROWID、TABLE ACCESS BY USER ROWID),索引的主要访问方式(INDEX UNIQUE SCAN、INDEX RANGE SCAN、INDEX FULL SCAN、INDEX FAST FULL SCAN、INDEX SKIP SCAN),多表查询还需要清楚表连接的方法(NESTED LOOP JOIN、SORT MERGE JOIN、HASH JOIN、MERGE JOIN CARTESIAN)和顺序(先访问驱动表)等知识;

  • Name

    这一列,就是操作的具体对象名称;

  • Starts

    这一列,代表访问次数;比如在NESTED LOOP JOIN中,被驱动表的具体访问次数就可以依据Starts的数值来判断。

  • E-Rows

    预估返回行数;

  • A-Rows

    实际返回行数;

  • A-Time

    实际执行时间;

  • Buffers

    逻辑读块/次;

如果是其他方式,看到的执行计划内容有所不同,比如可能就是下面这样:

Execution Plan
----------------------------------------------------------
Plan hash value: 1121436124 ----------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 38 | 2 (0)| 00:00:01 |
| 1 | TABLE ACCESS FULL | DUAL | 1 | 2 | 2 (0)| 00:00:01 |
| 2 | NESTED LOOPS | | 1 | 38 | 2 (0)| 00:00:01 |
| 3 | TABLE ACCESS BY INDEX ROWID| EMP | 1 | 25 | 1 (0)| 00:00:01 |
|* 4 | INDEX UNIQUE SCAN | PK_EMP | 1 | | 0 (0)| 00:00:01 |
| 5 | TABLE ACCESS BY INDEX ROWID| DEPT | 1 | 13 | 1 (0)| 00:00:01 |
|* 6 | INDEX UNIQUE SCAN | PK_DEPT | 1 | | 0 (0)| 00:00:01 |
----------------------------------------------------------------------------------------

可以看到,前三列没有区别,后面只有Rows、Bytes、Cost (%CPU)、Time等信息。

3.了解执行计划相关的信息

执行计划下面还有相关的信息,类似如下:

Predicate Information (identified by operation id):
--------------------------------------------------- 4 - access("EMPNO"=7788)
6 - access("A"."DEPTNO"="B"."DEPTNO") Note
-----
- SQL plan baseline SQL_PLAN_9sdj6nc4ybu5x2b78d17a used for this statement Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
6 consistent gets
0 physical reads
0 redo size
879 bytes sent via SQL*Net to client
520 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1 rows processed
  • 谓词信息(Predicate Information)

    标识出哪一步有access、filter这样的操作;

  • Note部分(Note)

    如果SQL执行中用到了动态采样、基数反馈、自适应游标共享、SQL_Profile、SPM等,会在这里显示出来;

  • 统计信息(Statistics)

    这里指的是SQL执行实际发生的递归调用、逻辑读、物理读、排序、处理行数等统计信息。

Oracle之SQL优化专题03-如何看懂SQL的执行计划的更多相关文章

  1. Oracle之SQL优化专题01-查看SQL执行计划的方法

    在我2014年总结的"SQL Tuning 基础概述"中,其实已经介绍了一些查看SQL执行计划的方法,但是不够系统和全面,所以本次SQL优化专题,就首先要系统的介绍一下查看SQL执 ...

  2. 【重磅干货】看了此文,Oracle SQL优化文章不必再看!

    目录 SQL优化的本质 SQL优化Road Map 2.1 制定SQL优化目标 2.2 检查执行计划 2.3 检查统计信息 2.4 检查高效访问结构 2.5 检查影响优化器的参数 2.6 SQL语句编 ...

  3. 梁敬彬老师的《收获,不止SQL优化》,关于如何缩短SQL调优时间,给出了三个步骤,

    梁敬彬老师的<收获,不止SQL优化>,关于如何缩短SQL调优时间,给出了三个步骤, 1. 先获取有助调优的数据库整体信息 2. 快速获取SQL运行台前信息 3. 快速获取SQL关联幕后信息 ...

  4. SQL Server中参数化SQL写法遇到parameter sniff ,导致不合理执行计划重用的一种解决方案

    parameter sniff问题是重用其他参数生成的执行计划,导致当前参数采用该执行计划非最优化的现象.想必熟悉数据的同学都应该知道,产生parameter sniff最典型的问题就是使用了参数化的 ...

  5. Oracle之SQL优化专题02-稳固SQL执行计划的方法

    首先构建一个简单的测试用例来实际演示: create table emp as select * from scott.emp; create table dept as select * from ...

  6. 数据库sql优化总结之5--数据库SQL优化大总结

    数据库SQL优化大总结 小编最近几天一直未出新技术点,是因为小编在忙着总结整理数据库的一些优化方案,特此奉上,优化总结较多,建议分段去消化,一口吃不成pang(胖)纸 一.百万级数据库优化方案 1.对 ...

  7. 学习如何看懂SQL Server执行计划(三)——连接查询篇

    三.连接查询部分 --------------------嵌套循环-------------------- /* UserInfo表数据少.Coupon表数据多嵌套循环可以理解为就是两层For循环,外 ...

  8. 学习如何看懂SQL Server执行计划(二)——函数计算篇

    二.函数计算部分 --------------------标量聚合--------------------/* 标量聚合-主要在聚合函数操作中产生 计算标量:根据行中的现有值计算出一个新值 流聚合:在 ...

  9. 学习如何看懂SQL Server执行计划(一)——数据查询篇

    一.数据查询部分 1. 看到执行计划有两种方式,对sql语句按Ctrl+L,或按Ctrl+M打开显示执行计划窗口每次执行sql都会显示出相应的执行计划 2. 执行计划的图表是从右向左看的 3. SQL ...

随机推荐

  1. ABC155E - Payment

    简述题意,给你一个大数,你可以选择10的次幂进行加减运算,问如何用最少的次数从0到达这个大数 考虑从这个大数到0,从最低位开始,每次都将这个位置取完,2种策略,贪心的话不好处理进位的情况,可以想到是D ...

  2. 查漏补缺之Go的Strings, bytes, runes和字符

    字节遍历,字符遍历 https://play.golang.org/p/DeZcCN9aHXo package main import ( "fmt" "unicode/ ...

  3. JavaScript动画实例:李萨如曲线

    在“JavaScript图形实例:阿基米德螺线”和“JavaScript图形实例:曲线方程”中,我们学习了利用曲线的方程绘制曲线的方法.如果想看看曲线是怎样绘制出来的,怎么办呢?编写简单的动画,就可以 ...

  4. reactor---元数据驱动的表单

    class NameForm extends React.Component {  constructor(props) {    super(props);    this.state = {    ...

  5. hdu 2838 Cow Sorting 树状数组求所有比x小的数的个数

    Cow Sorting Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total ...

  6. TCP 3次握手 && 4次分手

    原文:https://github.com/jawil/blog/issues/14 3次握手 第一次握手:建立连接.客户端发送连接请求报文段,将SYN位置为1,Sequence Number为x:然 ...

  7. C语言入门书籍知识点记录

    1. 数据在内存中的存储(二进制存储) 内存条:电路的电压有两种状态:0V或者5V,对应的一个元器件有2种状态:0 或者1. 一般情况下我们不一个一个的使用元器件,而是将8个元器件看做一个单位. 一个 ...

  8. RTL级低功耗设计

    重点:门控时钟和操作数隔离 1.并行与流水的选择 并行与流水都是用面积换速度,但是有时可以降低功耗 并行处理常用于数字信号处理部分,采用并行处理,也已降低系统工作频率从而降低功耗 用两个乘法器取代原设 ...

  9. Linux设备树学习

    1.概念 设备树用于实现驱动代码与设备信息相分离.驱动代码只负责处理驱动的逻辑而关于设备的具体信息存放到设备树文件中.(dts文件,编译后为dtb文件).一个dts文件对应一个ARM的machine, ...

  10. Gridview的stretchMode详解附自动宽度

    <GridView android:id="@+id/grid" android:layout_width="fill_parent" android:l ...