【SQLServer】SQLServer执行计划运算符-第2部分
2022-09-29 09:30 abce 阅读(380) 评论(0) 编辑 收藏 举报4.【SQLServer】SQLServer执行计划运算符-第1部分
让我们首先创建下表并用3K记录填充它,以便在本文的示例中使用它。可以使用以下T-SQL脚本创建该表并用数据填充该表:
CREATE TABLE ExPlanOperator_P2 ( ID INT IDENTITY (1,1), EmpFirst_Name VARCHAR(50), EmpLast_name VARCHAR(50), EmpAddress VARCHAR(MAX), EmpPhoneNum varchar(50) ) GO INSERT INTO ExPlanOperator_P2 VALUES ('AB','BA','CB','123123') GO 1000 INSERT INTO ExPlanOperator_P2 VALUES ('DA','EB','FC','456456') GO 1000 INSERT INTO ExPlanOperator_P2 VALUES ('DC','EA','FB','789789') GO 1000
SQL Server RID查找运算符(RID Lookup Operator)
假设我们在ExPlanOperator_P2表的EmpFirst_Name列上有一个非聚集索引,该索引是使用下面的CREATE INDEX T-SQL语句创建的:
CREATE INDEX IX_ExPlanOperator_P2_EmpFirst_Name ON ExPlanOperator_P2 (EmpFirst_Name)
如果执行以下查询并获取真正的执行计划:
SELECT * FROM ExPlanOperator_P2 WHERE EmpFirst_Name = 'BB'
查看执行查询后生成的SQL Server执行计划,会看到SQL Server查询优化器将使用非聚集索引来查找EmpFirst_Name值等于 'BB' 的所有员工,而无需扫描全表。另一方面,SQL Server引擎将无法从该非聚集索引中检索所有请求的值,因为查询请求该记录的所有列。回想一下,非聚集索引仅包含键列值和指向基表中该键的其余列的指针。
由于该表不包含聚集索引,因此该表将被视为没有对其页进行排序和对页内的数据进行排序的堆表。
因此,SQL Server引擎将使用非聚集索引中的指针(指向基表上其余列的位置)来定位和检索基础表中的其余列,使用Nested Loops运算符将Index Seek数据与从RID Lookup运算符(也称为Row Identifier运算符)检索到的数据集连接起来,如下面的SQL Server执行计划所示:
RID是一个行定位器,包括有关该记录位置的信息,例如数据库文件、页、有助于快速识别行位置的槽号。如果将鼠标移动到生成的SQL Server执行计划上的RID Lookup以查看该运算符的提示,将在输出列表中看到查询请求并由该运算符返回的列的列表,因为这些列不位于非聚集索引中,如下所示:
如果查看SQL Server执行计划中的RID Lookup运算符,会发现该运算符的成本与计划的整体权重相关,在我们的示例中为50%。这是由于在将其与嵌套循环操作组合之前执行的两个不同操作而不是单个操作的额外I/O开销。在处理少量行时可以忽略此开销。但是,如果正在处理大量记录,则最好调整该查询,通过限制检索到的列重写查询或为该查询创建覆盖索引。如果通过创建覆盖索引来消除RID查找,则此SQL Server执行计划中将不需要嵌套循环运算符。
SQL Server键查找运算符(Key Lookup Operator)
Key Lookup运算符是上一节中描述的RID Lookup运算符的集群等效项。假设我们使用下面的CREATE INDEX T-SQL语句在EmpFirst_Name表的ID列上创建了以下聚集索引:
CREATE CLUSTERED INDEX IX_ExPlanOperator_P2_ID on ExPlanOperator_P2 (ID)
如果执行以下的查询并查看生成的真正的执行计划:
SELECT * FROM ExPlanOperator_P2 WHERE EmpFirst_Name = 'BB'
检查执行查询后生成的SQL Server执行计划,会注意到SQL Server引擎对非聚集索引执行了查找操作,以检索EmpFirst_Name列值等于“BB”的所有员工。但同样,并非所有列都可以从该非聚集索引中检索到。因此,SQL Server引擎将受益于存在于该非聚集索引上的指针,这些指针指向基础表中的其余列。而且由于这个表是一个聚集表,它有一个聚集索引对该表的数据进行排序,Non_Clustered索引指针将指向聚集索引而不是指向基础表。其余列将使用嵌套循环运算符检索,以将索引查找数据与从键查找运算符检索的数据连接起来,因为SQL Server引擎无法一次性检索行。也就是说,SQL Server Engine会使用聚集索引键作为参考,使用非聚集索引中存储的聚集键值来查找聚集索引中存储的数据,如SQL Server执行中所示计划如下:
与RID Lookup运算符类似,Key Lookup代价非常昂贵,因为它需要额外的I/O开销,具体取决于记录的数量。此外,键查找运算符表明,需要索引覆盖或包含索引来消除对键查找和嵌套循环运算符的需要来提高该查询的性能。例如,如果我们在现有的非聚集索引中包含所有必需的列,使用下面的CREATE INDEX T-SQL 语句:
CREATE INDEX IX_ExPlanOperator_P2_EmpFirst_Name ON ExPlanOperator_P2 (EmpFirst_Name) INCLUDE (ID,EmpLast_name, EmpAddress, EmpPhoneNum) WITH (DROP_EXISTING = ON)
接下来运行前面的SELECT 语句。将看到不再使用Key Lookup和Nested Loops运算符,因为SQL Server引擎将通过查找非聚集索引来检索所有请求的数据,如下所示:
SQLServer排序操作符(Sort Operator)
假设我们运行以下SELECT语句,该语句从ExPlanOperator_P2表中返回具有特定EmpFirst_Name值的员工列表,使用ORDER BY子句按EmpLast_Name列值降序排序:
SELECT * FROM ExPlanOperator_P2 WHERE EmpFirst_Name = 'BB' ORDER BY EmpLast_name desc
检查执行查询后生成的SQL Server执行计划,会看到SQL Server Engine寻找Non-Clustered索引以检索请求的数据,然后Index Seek运算符的输出将被传送到SORT运算符以按照ORDER BY子句中指定的方式对数据进行排序,如果未指定,则使用默认的ASC排序方向。
在我们的例子中,SQL Server执行计划如下:
如果移动鼠标指向SORT操作符,会看到SORT操作符的输出是相同的输入列,但按指定列排序,如下图所示:
从SQL Server执行计划中可以看出,SORT运算符是一个开销很大的运算符。在我们的例子中,SORT运算符的成本是总查询成本的78%。此成本是由于ORDER BY子句中指定的列没有在请求的订单上定义的索引。因此,需要始终考虑是否真的需要这种排序操作,如果可以没有它,最好不要使用它或创建索引来获得该列的排序副本,从而消除SORT运算符的开销。