专题第一篇《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. (3)LoraWAN:链路控制、SF BW CR

    三.Introduction on LoRaWAN options 本文件描述了一种用于可为移动的或固定在一个固定位置的电池供电的终端设备而优化的LoRaWAN™网络协议.LORA™是一个由Semte ...

  2. redis学习笔记-01:redis简介

    1.redis是一个高性能的Nosql数据库,遵守BSD协议,使用c语言编写.支持网络.可基于内存亦可持久化,是一种日志型.Key-Value数据库,也可看做是一个分布式的.基于内存的缓存工具. 2. ...

  3. Calendar 时间类的应用

    Date 类最主要的作用就是获得当前时间,同时这个类里面也具有设置时间以及一些其他的功能,但是由于本身设计的问题,这些方法却遭到众多批评,不建议使用,更推荐使用 Calendar 类进行时间和日期的处 ...

  4. redis、mongodb、memcache安装好后设置开机自启动

    vim /etc/rc.d/rc.local /usr/local/mongodb/bin/mongod --smallfiles /usr/local/bin/redis-server/usr/lo ...

  5. 使用总结:java多线程总结 <转载>

    转载 https://www.cnblogs.com/rollenholt/archive/2011/08/28/2156357.html java多线程总结 2011-08-28 20:08  Ro ...

  6. Verilog的非阻塞语句放到顺序块中,综合出来怎样的逻辑电路?

    情境: FPGA里面计数器需要复位(计数值置零),与计数器状态有关的行为是状态机控制的,即状态机为CLEAR_TIMER状态时,计数器才完成清零动作. 清零有两个条件:(1)计数器值溢出(达到OVF门 ...

  7. Shortest and Longest LIS

    Codeforces Round #620 (Div. 2) D. Shortest and Longest LIS 题解: 贪心即可,对于最短序列,我们尽可能用可用的最大数字放入序列中,对于最长序列 ...

  8. Vue 实现分页效果

    分页,是在业务中经常要用到,为了节省用户流量和提升用户体验 讲一下思路: 首先是定义页号currentPage 和 页大小pagesize,用一个数组保存总数据: 用一个计算属性page_arrs,作 ...

  9. P1058 选择题

    P1058 选择题 转跳点:

  10. Windows和linux命令行快捷键

    Powershell的快捷键和cmd,linux中的shell,都比较像. ALT+F7 清除命令的历史记录 PgUp PgDn 显示当前会话的第一个命令和最后一个命令 Enter 执行当前命令 En ...