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

SQL Server 中的执行计划支持 SELECT、INSERT、UPDATE、DELETE 以及 EXECUTE 语句 。
SQL Server 执行计划各个步骤的执行顺序按照缩进来判断 , 缩进越多的越先执行 , 同样缩进的从上至下执行 。接下来 , 我们需要理解执行计划中各种信息的含义:

  • Rows 表示该步骤实际产生的记录数;
  • Executes 表示该步骤实际被执行的次数;
  • StmtText 包含了每个步骤的具体描述 , 也就是如何访问和过滤表的数据、如何实现表的连接、如何进行排序操作等;
  • StmtId , 该语句的编号;
  • NodeId , 当前操作步骤的节点号 , 不代表执行顺序;
  • Parent , 当前操作步骤的父节点 , 先执行子节点 , 再执行父节点;
  • PhysicalOp , 物理操作 , 例如连接操作的嵌套循环实现;
  • LogicalOp , 逻辑操作 , 例如内连接操作;
  • Argument , 操作使用的参数;
  • DefinedValues , 定义的变量值;
  • EstimateRows , 估计返回的行数;
  • EstimateIO , 估计的 IO 成本;
  • EstimateCPU , 估计的 CPU 成本;
  • AvgRowSize , 平均返回的行大小;
  • TotalSubtreeCost , 当前节点累计的成本;
  • OutputList , 当前节点输出的字段列表;
  • Warnings , 预估得到的警告信息;
  • Type , 当前操作步骤的类型;
  • Parallel , 是否并行执行;
  • EstimateExecutions , 该步骤预计被执行的次数;
对于上面的语句 , 节点执行的顺序为 3 -> 4 -> 2 -> 1 。首先执行第 3 行 , 通过聚集索引(主键)扫描 employees 表加过滤的方式返回了 3 行数据 , 估计的行数(3.0841121673583984)与此非常接近;然后执行第 4 行 , 循环使用聚集索引的方式查找 departments 表 , 循环 3 次每次返回 1 行数据;第 2 行是它们的父节点 , 表示使用 Nested Loops 方式实现 Inner Join , Argument 列(OUTER REFERENCES:([e].[department_id]))说明驱动表为 employees ;第 1 行代表了整个查询 , 不执行实际操作 。
最后 , 可以使用以下命令关闭语句的分析功能:
sqlite> EXPLAIN QUERY PLAN...> 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;QUERY PLAN|--SCAN TABLE employees AS e`--SEARCH TABLE departments AS d USING INTEGER PRIMARY KEY (rowid=?)关于 SQL Server 执行计划和 SQL 调优 , 可以参考 SQL Server 官方文档执行计划 。
 
PostgreSQL 执行计划
 
PostgreSQL 中获取执行计划的方法与 MySQL 类似 , 也就是在 SQL 语句的前面加上EXPLAIN关键字:
sqlite> EXPLAIN...> 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;addropcodep1p2p3p4p5comment---------------------------------------------------------0Init015000Start at 151OpenRead0501100root=5 iDb=0; employees2OpenRead120200root=2 iDb=0; departments3Rewind0140004Column07100r[1]=employees.salary5Le2131(BINARY)53if r[1]<=r[2] goto 136Column010300r[3]=employees.department_id7SeekRowid113300intkey=r[3]8Column01400r[4]=employees.first_name9Column02500r[5]=employees.last_name10Column07600r[6]=employees.salary11Column11700r[7]=departments.department_name12ResultRow44000output=r[4..7]13Next0400114Halt0000015Transaction008001usesStmtJournal=016Integer150002000r[2]=1500017Goto01000PostgreSQL 中的EXPLAIN支持 SELECT、INSERT、UPDATE、DELETE、VALUES、EXECUTE、DECLARE、CREATE TABLE AS 以及 CREATE MATERIALIZED VIEW AS 语句 。
PostgreSQL 执行计划的顺序按照缩进来判断 , 缩进越多的越先执行 , 同样缩进的从上至下执行 。对于以上示例 , 首先对 employees 表执行全表扫描(Seq Scan) , 使用 salary > 15000 作为过滤条件;cost 分别显示了预估的返回第一行的成本(0.00)和返回所有行的成本(3.34);rows 表示预估返回的行数;width 表示预估返回行的大小(单位 Byte) 。然后将扫描结果放入到内存哈希表中 , 两个 cost 都等于 3.34 , 因为是在扫描完所有数据后一次性计算并存入哈希表 。接下来扫描 departments 并且根据 department_id 计算哈希值 , 然后和前面的哈希表进行匹配(d.department_id = e.department_id) 。最上面的一行表明数据库采用的是 Hash Join 实现连接操作 。


推荐阅读