解释执行计划
警告 并行处理会使执行计划的解释更加困难。原因很简单:多个操作并发执行。为了保持叙述尽可能简单,本节并不涵盖并行处理的内容。
父-子关系
执行计划是一棵树,用来描述SQL引擎执行操作的顺序以及各个操作之间的关系。树中的每个节点是一个行源操作(实际上是作为用C语言编写的一个函数执行的),例如,表扫描、联接或排序。在各操作(节点)之间,存在着父子关系。理解这些关系对于正确阅读执行计划非常关键。当执行计划以文本格式显示时,控制父-子关系的规则如下所示。
Ø 一个父操作拥有一个或多个子操作。
Ø 一个子操作只有一个父操作。
Ø 唯一没有父操作的操作是树的根操作(顶层操作)。
Ø 子操作跟随着它们的父操作,在右侧缩进排列。依赖于显示执行计划使用的方法,缩进可以是一个空格字符、两个空格或其他什么。关键是同一个父操作下的所有子操作都拥有相同的缩进。
Ø 父操作在子操作之前出现(父操作的ID比子操作的ID要小)。如果一个子操作前面有多个与父操作一样缩进的操作,则距离最近的操作为父操作。
接下来是一个由relationship.sql脚本生成的样例执行计划。注意,尽管只有Operation列需要贯穿整个执行计划,Id列出现在这里是为了帮助你更容易地标识操作。

图10-2提供了执行计划的图示。使用之前描述的规则,你可以推断出以下内容。
Ø 操作0是这棵树的根。它告知你执行计划关联的SQL语句的类型。
Ø 操作0有一个子操作:操作1。
Ø 操作1有三个子操作:2、5,还有8。
Ø 操作2有两个子操作:3和4。
Ø 操作3和4没有子操作。
Ø 操作5有一个子操作:6。
Ø 操作6有一个子操作:7。
Ø 操作7没有子操作。
Ø 操作8有一个子操作:9。
Ø 操作9没有子操作。

了解父-子关系对于理解执行计划执行各个操作的顺序十分关键。实际上,为了完成它们的任务,父操作需要由它们的子操作提供的数据。因此,虽然执行是从树的根部开始的,第一个被完全执行的操作是没有子操作的那个,所以,是树的叶节点。为验证这一点,我们看一下接下来的这个执行计划:

操作按以下顺序执行。
(1)执行计划的入点是操作0,它是树的根操作。但是,操作0是一个select语句,没有数据可供操作。因此,它必须调用它的子操作(1)。
(2)操作1是一个排序操作,没有数据可供操作。因此,它必须调用它的子操作(2)。
(3)操作2是一个表扫描,需要rowid来访问t表。因此,它必须调用它的子操作(3)。
(4)操作3是一个索引扫描,不需要来自其他操作的数据(它没有子操作)。因此,它在t_pk索引上执行索引范围扫描并将它找到的rowid传递给父操作(2)。
(5)操作2使用从它的子操作(3)接收的rowid列表去访问t表。然后,将结果数据传递给它的父操作(1)。
(6)操作1对它的子操作(2)传递过来的数据进行排序,然后将排序后的数据传递给它的父操作(0)。
(7)操作0将从它的子操作(1)接收的数据传递给调用者。
注意 尽管第一个被执行的操作永远是树的根操作,但是父操作(上面的例子中是三个)可能除了调用子操作以外什么都不做。所以,为简单起见,我通常会说执行是从第一个做实际工作的操作(上面的例子中是操作3)开始的。
下面的三条通用规则总结了刚刚描述的行为。
Ø 父操作调用子操作。
Ø 子操作在它们的父操作之前被完全执行。
Ø 子操作向它们的父操作传递数据。




