本系列的前几篇文章中, SQL Server 执行计划概述、 SQL Server 执行计划类型和如何分析 SQL 图形执行计划组,我们讨论了 SQL Server 关系引擎为生成执行计划而执行的步骤以及 SQL Server 存储引擎为检索请求的数据或执行修改操作而执行的步骤。
此外,我们阐明了可用于排除查询性能故障的 SQL Server的 查询执行计划的不同类型和格式。最后,我们讨论了图形查询计划的组件以及我们如何对其进行分析。在本文中,我们将介绍第一组 SQL 查询计划运算符。
在开始使用查询执行计划运算符之前,我们将创建一个新的简单表并使用以下 T-SQL 脚本填充测试数据:
CREATE TABLE ExPlanOperator
( ID INT IDENTITY (1,1),
First_Name VARCHAR(50),
Last_name VARCHAR(50),
Address VARCHAR(MAX)
)
GO
INSERT INTO ExPlanOperator VALUES ('AA','BB','CC')
GO 1000
INSERT INTO ExPlanOperator VALUES ('DD','EE','FF')
GO 1000
表扫描运算符(Table Scan)
上方创建的 ExPlanOperator 表是一个堆表(HeapTable)。因为没有在表上创建聚集索引,所以表中的数据行在每个数据页中没有以任何特定顺序存储,
因此,为了返回对该表的任何查询的结果,SQL Server 引擎将使用表扫描运算符逐行扫描整个表。
假设我们运行下面的 T-SQL 语句,运行时选择包含实际执行计划的选项,以查看它将如何运行。从生成的 SQL 查询执行计划中可以看到,SQL Server 引擎使用了**表扫描运算符(TableScan)**扫描整个表的所有数据行,以检索请求的数据。如下图所示:
当我们在查询语句中添加 WHERE 子句来获取特定的记录,SQL Server 引擎的行为方式也是一样的。它还是使用了**表扫描运算符(TableScan)**来扫描整个表的所有数据行,因为该表上没有创建索引来协助数据检索。如下图所示:
如果我们在测试表的 ID 列上追加创建一个非聚集索引,使用下面的 CREATE INDEX T-SQL 语句:
CREATE INDEX IX_ExPlanOperator_ID ON ExPlanOperator (ID)
然后运行相同的 SELECT 语句来检索一组特定的 ID。您将看到 SQL Server 引擎并没有使用新创建的索引来查询数据,而是还是选择了**表扫描运算符(TableScan)**来扫描所有数据行。造成这种行为的原因有很多。例如,索引没有作用,表只包含少量数据行或查询操作将返回大部分数据行。如下图所示:
聚集索引扫描运算符(Clustered Index Scan)
在表上创建聚集索引(Clustered Index)时,该表将从堆表(HeapTable)转换为聚集表(Clustered Table)。聚簇表是在表的一列或多列上具有预定义聚簇索引的表,该索引根据聚簇索引键定义数据页内行的存储顺序和表内页的顺序。
有关群集表结构的详细信息,请参阅SQL Server 表结构概述。有关聚集索引的详细信息,请参阅设计有效的 SQL Server 聚集索引。
让我们删除之前在 ExPlanOperator 表上创建的非聚集索引,并使用以下 T-SQL 脚本将其替换为 ExPlanOperator 表中 ID 列上的聚集索引:
DROP INDEX IX_ExPlanOperator_ID on ExPlanOperator
CREATE CLUSTERED INDEX IX_ExPlanOperator_ID ON ExPlanOperator (ID)
如果我们再次运行相同的 SELECT 查询,从我们的测试表中检索所有行,你会看到表扫描运算符(Table Scan) 被 聚集索引扫描运算符(Clustered Index Scan) 替换了,同时箭头厚度与表扫描操作时的箭头厚度是一样的。如下图所示:
在上面的 SQL 查询执行计划中,SQL Server 引擎决定使用聚集索引扫描运算符,尽管它会遍历所有索引数据行。这样做的原因是,聚集索引除了可以作为索引键使用之外,还是底层表数据的排序替换。因为底层表将所有表数据都存储在了索引的最低级别-叶节点中。在统计信息没有包含有用的非聚集索引,或者查询将返回全部或大部分数据行的情况,SQL Server 引擎将会判断使用聚集索引扫描运算符扫描所有聚集索引行,比使用索引提供的键来查找更快。
聚集索引查找运算符(Clustered Index Seek)
如果通过在 WHERE 子句中添加数据过滤语句来修改先前的 SELECT 查询以提高效率,这限制了从表返回的行数。这种情况下,SQL Server 查询优化器会决定使用**聚集索引查找运算符(Clustered Index Seek)**来更高速检索数据。示意图如下:
SQL Server 查询优化器将定位到可用的聚集索引,通过向SQL Server 存储引擎直接提供索引键值定位信息,可以快速导航并定位到目标数据行,而不需要遍历所有的表数据行。另外使用聚集索引查找运算符(Clustered Index Seek)时,不需要额外的步骤来检索其余的数据列,因为表的完整排序副本存储在聚集索引的叶级节点中。
非聚集索引查找运算符(Non-clustered Index Seek)
当 SQL Server 查询优化器发现查询请求的所有数据都可以由可用索引提供时,它将使用索引查找运算符对该**非聚集索引(Non-clustered Index)**执行查找。
有关非聚集索引的详细信息,请参阅设计有效的 SQL Server 聚集索引。
使用下面的 CREATE INDEX T-SQL 命令在 ExPlanOperator 测试表的 First_Name 列上创建了一个非聚集索引:
CREATE INDEX IX_ExPlanOperator_FirstName on ExPlanOperator(First_Name)
然后运行以下 SELECT 语句来检索 First_Name 列。执行结果及执行计划的图示如下:
从 SQL 查询执行计划中看到,SQL Server 查询优化器发现创建的索引已经包含查询请求的所有数据,并且检索这些数据的最快方法是寻找非聚集索引,所以使用非聚集索引查找运算符(Non-clustered Index Seek)。非聚集索引中仅存储索引键值和指向其余列的指针,如果 SQL Server 查询优化器无法在该索引中找到所有请求的数据,它将在聚集索引(聚集表的情况)或基础表中(堆表的情况)中查找附加数据。但是在查找过程中执行的额外 I/O 操作会影响查询性能,我们将在本系列的后面文章中介绍这种情况。
在本系列的下一篇文章中,我们将讨论第二组 SQL Server 查询执行计划运算符。敬请关注!
系列目录
SQL Server 执行计划(1) - 概述
SQL Server 执行计划(2) - 如何查看执行计划
SQL Server 执行计划(3) - 如何分析图形执行计划
SQL Server 执行计划(4) - 执行计划运算符详解1
SQL Server 执行计划(5) - 执行计划运算符详解2
SQL Server 执行计划(6) - 执行计划运算符详解3
SQL Server 执行计划(7) - 执行计划运算符详解4
SQL Server 执行计划(8) - 使用执行计划进行查询性能调优
[SQL Server 执行计划(9) - 保存和比较执行计划]