一次搞定各种数据库SQL执行计划( 二 )


SELECT * FROM TABLE(DBMS_XPLAN.display);PLAN_TABLE_OUTPUT|--------------------------------------------------------------------------------------------|Plan hash value: 1343509718||--------------------------------------------------------------------------------------------|| Id| Operation| Name| Rows| Bytes | Cost (%CPU)| Time||--------------------------------------------------------------------------------------------||0 | SELECT STATEMENT||44 |1672 |6(17)| 00:00:01 |||1 |MERGE JOIN||44 |1672 |6(17)| 00:00:01 |||2 |TABLE ACCESS BY INDEX ROWID| DEPARTMENTS |27 |432 |2(0)| 00:00:01 |||3 |INDEX FULL SCAN| DEPT_ID_PK|27 ||1(0)| 00:00:01 |||*4 |SORT JOIN||44 |968 |4(25)| 00:00:01 |||*5 |TABLE ACCESS FULL| EMPLOYEES|44 |968 |3(0)| 00:00:01 ||--------------------------------------------------------------------------------------------||Predicate Information (identified by operation id):|---------------------------------------------------||4 - access("E"."DEPARTMENT_ID"="D"."DEPARTMENT_ID")|filter("E"."DEPARTMENT_ID"="D"."DEPARTMENT_ID")|5 - filter("E"."SALARY">15000)|Oracle 中的EXPLAIN PLAN FOR支持 SELECT、UPDATE、INSERT 以及 DELETE 语句 。
接下来 , 我们同样需要理解执行计划中各种信息的含义:

  • Plan hash value 是该语句的哈希值 。SQL 语句和执行计划会存储在库缓存中 , 哈希值相同的语句可以重用已有的执行计划 , 也就是软解析;
  • Id 是一个序号 , 但不代表执行的顺序 。执行的顺序按照缩进来判断 , 缩进越多的越先执行 , 同样缩进的从上至下执行 。Id 前面的星号表示使用了谓词判断 , 参考下面的 Predicate Information;
  • Operation 表示当前的操作 , 也就是如何访问表的数据、如何实现表的连接、如何进行排序操作等;
  • Name 显示了访问的表名、索引名或者子查询等 , 前提是当前操作涉及到了这些对象;
  • Rows 是 Oracle 估计的当前操作返回的行数 , 也叫基数(Cardinality);
  • Bytes 是 Oracle 估计的当前操作涉及的数据量
  • Cost (%CPU) 是 Oracle 计算执行该操作所需的代价;
  • Time 是 Oracle 估计执行该操作所需的时间;
  • Predicate Information 显示与 Id 相关的谓词信息 。access 是访问条件 , 影响到数据的访问方式(扫描表还是通过索引);filter 是过滤条件 , 获取数据后根据该条件进行过滤 。
在上面的示例中 , Id 的执行顺序依次为 3 -> 2 -> 5 -> 4- >1 。首先 , Id = 3 扫描主键索引 DEPT_ID_PK , Id = 2 按主键 ROWID 访问表 DEPARTMENTS , 结果已经排序;其次 , Id = 5 全表扫描访问 EMPLOYEES 并且利用 filter 过滤数据 , Id = 4 基于部门编号进行排序和过滤;最后 Id = 1 执行合并连接 。显然 , 此处 Oracle 选择了排序合并连接的方式实现两个表的连接 。
关于 Oracle 执行计划和 SQL 调优 , 可以参考 Oracle 官方文档《SQL Tuning Guide》(https://docs.oracle.com/en/database/oracle/oracle-database/19/tgsql/) 。
SQL Server 执行计划
SQL Server Management Studio 提供了查看图形化执行计划的简单方法 , 这里我们介绍一种通过命令查看的方法:
SET STATISTICS PROFILE ON以上命令可以打开 SQL Server 语句的分析功能 , 打开之后执行的语句会额外返回相应的执行计划:
SELECT e.first_name,e.last_name,e.salary,d.department_nameFROM employees eJOIN departments d ON (e.department_id = d.department_id) WHERE e.salary > 15000;first_name|last_name|salary|department_name|----------|---------|--------|---------------|Steven|King|24000.00|Executive|Neena|Kochhar|17000.00|Executive|Lex|De Haan|17000.00|Executive|Rows|Executes|StmtText|StmtId|NodeId|Parent|PhysicalOp|LogicalOp|Argument|DefinedValues|EstimateRows|EstimateIO|EstimateCPU|AvgRowSize|TotalSubtreeCost|OutputList|Warnings|Type|Parallel|EstimateExecutions|----|--------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------|------|------|--------------------|--------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------|------------|------------|-----------|----------|----------------|----------------------------------------------------------------------|--------|--------|--------|------------------|3|1|SELECT e.first_name,e.last_name,e.salary,d.department_name¶FROM employees e¶JOIN departments d ON (e.department_id = d.department_id)¶ WHERE e.salary > 15000|1|1|0|||||2.9719627||||0.007803641|||SELECT|0||3|1||--Nested Loops(Inner Join, OUTER REFERENCES:([e].[department_id]))|1|2|1|Nested Loops|Inner Join|OUTER REFERENCES:([e].[department_id])||2.9719627|0|0|57|0.007803641|[e].[first_name], [e].[last_name], [e].[salary], [d].[department_name]||PLAN_ROW|0|1|3|1||--Clustered Index Scan(OBJECT:([hrdb].[dbo].[employees].[emp_emp_id_pk] AS [e]), WHERE:([hrdb].[dbo].[employees].[salary] as [e].[salary]>(15000.00)))|1|3|2|Clustered Index Scan|Clustered Index Scan|OBJECT:([hrdb].[dbo].[employees].[emp_emp_id_pk] AS [e]), WHERE:([hrdb].[dbo].[employees].[salary] as [e].[salary]>(15000.00))|[e].[first_name], [e].[last_name], [e].[salary], [e].[department_id]|3|0.0038657407|2.747E-4|44|0.004140441|[e].[first_name], [e].[last_name], [e].[salary], [e].[department_id]||PLAN_ROW|0|1|3|3||--Clustered Index Seek(OBJECT:([hrdb].[dbo].[departments].[dept_id_pk] AS [d]), SEEK:([d].[department_id]=[hrdb].[dbo].[employees].[department_id] as [e].[department_id]) ORDERED FORWARD)|1|4|2|Clustered Index Seek|Clustered Index Seek|OBJECT:([hrdb].[dbo].[departments].[dept_id_pk] AS [d]), SEEK:([d].[department_id]=[hrdb].[dbo].[employees].[department_id] as [e].[department_id]) ORDERED FORWARD|[d].[department_name]|1|0.003125|1.581E-4|26|0.0035993|[d].[department_name]||PLAN_ROW|0|3|


推荐阅读