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

本文详细阐述了如何通过EXPLAINPLAN功能获取SQL语句的执行计划,包括多种获取方式及其应用方法,旨在提高数据库性能。通过了解执行计划,开发者能够优化SQL查询,减少资源消耗,提升查询效率。

SQL查询语句的性能从一定程度上影响整个数据库的性能。很多情况下,数据库性能的低下差不多都是不良SQL语句所引起。而SQL语句的执行
计划则决定了SQL语句将会采用何种方式从数据库提取数据并返回给客户端,本文描述的将是如何通过EXPLAIN PLAN 获取SQL语句执行计划来获
取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 /*+ parallel(t4,2) */ * 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标识符区分大小写。


<think>嗯,我现在要学习如何使用EXPLAIN来分析SQL语句执行计划。首先,我得弄清楚什么是EXPLAIN,它在SQL中的作用是什么。我记得以前听说过,EXPLAIN可以帮助查看查询的执行计划,但具体怎么用还不清楚。 首先,我应该了解基本语法。用户可能需要知道不同的数据库系统是否有差异。比如MySQL、PostgreSQLSQL Server可能用法不同。例如,MySQL使用EXPLAIN后面直接跟SQL语句,而PostgreSQL可能有更多的选项,比如ANALYZE。这时候需要分情况说明,但用户可能用的是其中一个,所以最好都涵盖到。 接下来,执行计划中的各个字段是什么意思呢?比如id、select_type、table、type、key、rows、Extra这些字段在MySQL中的含义。我需要解释每个字段的作用,比如type列显示了连接类型,是ALL还是index,这关系到性能问题。rows表示预估扫描的行数,key是实际使用的索引,这些都很关键。 然后,如何解读结果?用户可能需要知道怎么看执行计划是否高效。比如,如果看到type是ALL,说明全表扫描,可能需要加索引。如果Extra里有Using temporary或者Using filesort,可能意味着需要优化查询结构。这些点需要详细说明,帮助用户识别问题。 还有,不同数据库的关键字可能不同。比如SQL Server用的是SET SHOWPLAN_XML ON,而Oracle可能需要EXPLAIN PLAN FOR。这时候可能需要比较不同数据库的用法,但用户可能只关心其中一种,所以需要先确认,或者分别说明。 另外,实际例子会很有帮助。比如给出一个简单的SELECT语句,然后展示使用EXPLAIN后的输出,并逐步解释每个部分的意义。例如,分析一个带有JOIN的查询,看看执行顺序,索引使用情况等。 还需要提到高级用法,比如在PostgreSQL使用EXPLAIN ANALYZE会实际执行查询并给出时间消耗,而不仅仅是预估。这时候要注意在生产环境中谨慎使用,因为可能会影响性能。 可能的问题点:用户可能不知道如何根据执行计划进行优化,所以需要给出常见的优化建议,比如添加索引、避免全表扫描、优化JOIN顺序等。同时,提醒用户注意rows和实际执行的行数是否差异大,可能统计信息过时需要更新。 总结步骤的话,可能分为:1. 使用EXPLAIN语句获取执行计划;2. 理解各列的含义;3. 分析关键指标识别瓶颈;4. 根据结果进行优化。同时,需要强调不同数据库的差异,建议查阅对应文档。 现在需要验证这些信息是否正确。比如MySQLEXPLAIN输出字段是否准确,PostgreSQL的ANALYZE选项是否正确。如果有错误,可能会导致用户误解,所以必须确保准确性。可能需要回忆官方文档的内容,或者参考可靠的资料。 最后,整理成一个结构清晰的回答,分步骤说明,并给出示例和注意事项,帮助用户逐步掌握分析方法。</think>### 如何用 `EXPLAIN` 分析 SQL 语句执行计划? #### 1. **基本语法** 不同数据库的 `EXPLAIN` 语法略有差异: - **MySQL**:直接添加 `EXPLAIN` 前缀: ```sql EXPLAIN SELECT * FROM users WHERE age > 30; ``` - **PostgreSQL**:支持 `ANALYZE` 实际执行查询(慎用于生产环境): ```sql EXPLAIN ANALYZE SELECT * FROM users WHERE age > 30; ``` - **SQL Server**:使用 `SET SHOWPLAN_XML ON` 查看执行计划。 --- #### 2. **解读执行计划的关键字段(以 MySQL 为例)** 执行计划结果包含以下核心列: - **id**:查询的序列号,子查询时序号递增。 - **select_type**:查询类型(如 `SIMPLE` 简单查询,`SUBQUERY` 子查询)。 - **table**:涉及的表名。 - **type**:访问数据的方式: - `ALL`:全表扫描(需优化)。 - `index`:全索引扫描。 - `range`:索引范围扫描。 - `ref`:非唯一索引查找。 - `eq_ref`:唯一索引关联(如主键)。 - **possible_keys**:可能用到的索引。 - **key**:实际使用的索引。 - **rows**:预估扫描的行数。 - **Extra**:额外信息: - `Using where`:需过滤数据。 - `Using temporary`:使用临时表(可能需优化)。 - `Using filesort`:额外排序(考虑索引优化)。 --- #### 3. **分析步骤** - **步骤 1:定位全表扫描** 若 `type=ALL` 且 `rows` 值较大,说明未命中索引。 **优化**:为 `WHERE` 或 `JOIN` 字段添加索引。 - **步骤 2:检查索引使用情况** - `key` 列为空:未使用索引。 - `possible_keys` 有值但 `key` 为空:需强制索引或优化查询条件。 - **步骤 3:关注 `Extra` 列** - `Using filesort`:排序未利用索引,尝试为 `ORDER BY` 字段加索引。 - `Using temporary`:复杂 `GROUP BY` 或子查询,需简化逻辑。 --- #### 4. **示例分析** **SQL 语句**: ```sql EXPLAIN SELECT u.name, o.total FROM users u JOIN orders o ON u.id = o.user_id WHERE u.age > 30 ORDER BY u.name; ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值