4.【SQLServer】SQLServer执行计划运算符-第1部分
5.【SQLServer】SQLServer执行计划运算符-第2部分
实现一个包含3K记录的测试表,以在本文的示例中使用它。此演示表是使用以下T-SQL脚本创建并填充数据的:
CREATE TABLE ExPlanOperator_P3 ( ID INT IDENTITY(1, 1) ,STD_Name VARCHAR(50) ,STD_BirthDate DATETIME ,STD_Address VARCHAR(MAX) ,STD_Grade INT ) GO INSERT INTO ExPlanOperator_P3 VALUES ( 'AA' ,'1998-05-30' ,'BB' ,93 ) GO 1000 INSERT INTO ExPlanOperator_P3 VALUES ( 'CC' ,'1998-10-13' ,'DD' ,78 ) GO 1000 INSERT INTO ExPlanOperator_P3 VALUES ( 'EE' ,'1998-06-24' ,'FF' ,85 ) GO 1000
SQLServer聚合运算符-流聚合(Aggregate Operator–Stream Aggregate)
聚合运算符主要用于通过对聚合列的值进行分组,计算聚合表达式。聚合表达式包括MIN、MAX、COUNT、AVG、SUM操作。让我们运行下面的SELECT语句,检索获得最高成绩的前10名学生。我们将包括查询的实际SQL执行计划。这可以通过使用MAX聚合函数聚合学生的成绩来获得最高成绩来实现。如果需要这些优秀学生的所有信息,我们应该在GROUP BY子句中添加所有这些列,如下所示:
SELECT TOP 10 ID ,STD_Name ,STD_BirthDate ,STD_Address ,MAX(STD_Grade) FROM ExPlanOperator_P3 GROUP BY ID ,STD_Name ,STD_BirthDate ,STD_Address
检查执行计划,将看到SQL Server引擎将从表中检索行,然后根据GROUP BY子句中指定的列对这些值进行排序,并使用最快的聚合方法聚合这些值,即流聚合运算符。Stream Aggregate运算符速度很快,因为在聚合这些值之前根据GROUP BY子句中指定的列对行进行排序。如果行没有在Seek或Scan运算符中排序,SQL Server Engine将强制使用SORT 运算符,如下所示:
Stream Aggregate运算符的另一个示例是AVG聚合函数,它将计算聚合列的SUM和COUNT并将值存储在Expr1004和Expr1005中,以评估AVG值,如下所示:
SQLServer标量计算运算符(Compute Scalar Operator)
SQL Server标量计算运算符用于通过执行标量计算操作来从现有行值计算新值,从而产生计算值。这些标量计算包括标量值的转换或连接。
让我们运行下面的SELECT语句来生成一个描述每个学生成绩的句子。我们将包括查询的实际 SQL 执行计划:
SELECT STD_Name + '_ has achieved _ ' + cast(STD_Grade AS VARCHAR(50)) AS STD_Result FROM ExPlanOperator_P3
你将从执行查询后生成的执行计划中看到,SQL Server Engine使用Compute Scalar运算符对两个指定的列执行连接以返回一个新的标量值,如下所示:
从执行计划可以看出,Compute Scalar运算符并不是一个昂贵的操作,它只花费我们查询总权重的2%,造成的开销最小。
SQL Server连接运算符(Concatenation Operator)
SQL Server连接运算符按顺序获取一组或多组数据,并返回每个输入数据集中的所有记录。该运算符最流行的示例之一是UNION ALL语句。让我们运行下面的语句,它使用UNION ALL语句组合了两个SELECT语句的结果:
SELECT * FROM ExPlanOperator_P3 AS P1 UNION ALL SELECT * FROM ExPlanOperator_P3 AS P2
从执行计划可以看到两个select语句的结果被连接运算符结合在一起了。
SQLServer断言运算符(Assert Operator)
SQL Server Assert运算符用于验证插入的值是否满足表上先前定义的CHECK或FOREIGN KEY约束。假设我们在演示表上定义了以下约束,以确保仅将正值插入STD_Grade列:
ALTER TABLE ExPlanOperator_P3 ADD CONSTRAINT CK_Grade_Positive CHECK (STD_Grade >0)
如果你尝试执行以下INSERT语句:
INSERT INTO ExPlanOperator_P3 VALUES ('GG','1998-01-28','HH',74)
将从执行计划中看到,SQL Server Engine使用ASSERT运算符来验证为该学生插入的成绩是否满足定义的CHECK约束,如下所示:
SQL Server哈希匹配连接运算符(Hash Match Join Operator)
将两个表连接在一起时,SQL Server引擎将表的数据分成大小相等的类别,称为存储桶,以便快速访问这些数据。这种数据结构称为哈希表。它使用一种算法来处理数据并将其分布在存储桶中。该算法称为哈希函数。
假设我们创建了一个新表,该表记录学生的缺席情况,并将其填写如下:
CREATE TABLE ExPlanOperator_JOIN ( STD_ID INT ,STD_AbsenceDays INT ) INSERT INTO ExPlanOperator_JOIN VALUES ( 1 ,5 ) GO 100 INSERT INTO ExPlanOperator_JOIN VALUES ( 10 ,2 ) GO 100
我们将运行以下SELECT语句,将基础Student表与缺席表连接起来:
SELECT STD_Name ,STD_Grade ,STD_AbsenceDays FROM ExPlanOperator_P3 P3 INNER JOIN ExPlanOperator_JOIN AB ON P3.ID = AB.STD_ID
从执行计划中可以看到,SQL Server引擎在从两个连接表中读取数据后,使用了Hash Match Join运算符,其中它使用小表(也称为Probe)的数据填充哈希表表,然后处理第二个大表,也称为构建表,根据哈希表的值,加快对请求数据的访问,如下图:
SQL Server哈希匹配聚合运算符(Hash Match Aggregate operator)
SQL Server Hash Match Aggregate运算符用于处理未使用索引排序的大型表。它在内存中构建一个哈希表,为每条记录计算一个哈希值,然后扫描所有其他记录以查找该哈希键。如果哈希表中不存在该值,它将在该哈希表中创建一个新条目。这样,SQL Server Engine将保证每组数据只有一条记录。
运行下面的SELECT语句,该语句返回每个ID值的重复数:
SELECT ID ,COUNT(*) FROM ExPlanOperator_P3 GROUP BY ID
SQL Server合并连接运算符(Merge Join Operator)
SQL Server Merge Join运算符与不同类型的JOIN操作一起使用,但仅在两个JOIN数据集根据连接谓词排序时使用。在这种情况下,Merge Join运算符会同时读取两个输入数据集,比较后返回匹配的结果。
运行以下查询,将ExPlanOperator_P3表与其自身连接起来:
SELECT * FROM ExPlanOperator_P3 E1 INNER JOIN ExPlanOperator_P3 E2 ON E1.ID = E2.ID
从执行计划中可以看到,SQL Server Engine将扫描每个输入,根据ID列对数据进行排序,因为该表上还没有创建索引,然后将两个输入连接起来,现在使用快速合并连接运算符基于JOIN谓词进行排序,如下所示:
尽管Merge Join运算符实际上连接了已排序的表,但仍然存在开销,因为两个输入都将加载到内存中以进行比较。Merge Join运算符的成本是两个运算符输入的总和。
SQL Server嵌套循环连接运算符(Nested Loops Join Operator)
SQL Server查询优化器仅在外部输入表较小且内部输入表在连接谓词键上创建索引时才决定使用嵌套循环连接运算符。
假设我们在演示表上创建了以下两个索引,如下所示:
CREATE INDEX IX_ExPlanOperator_P3_ID ON ExPlanOperator_P3 (ID) GO CREATE INDEX IX_ExPlanOperator_JOIN ON ExPlanOperator_JOIN ( STD_ID ,STD_AbsenceDays ) WITH (DROP_EXISTING = ON)
然后运行下面的语句,通过将两个演示表连接在一起,返回超过几天缺课的学生:
SELECT E1.ID FROM ExPlanOperator_P3 E1 INNER JOIN ExPlanOperator_JOIN E2 ON E1.ID = E2.STD_ID WHERE E2.STD_AbsenceDays > 4
从执行计划中,可以看到SQL Server Engine使用Nested Loops Join运算符连接两个表,但对外部表每执行一次索引扫描操作,就对内部表执行100次Index Seek操作,如下图:
虽然Nested Loops运算符的成本与查询的整体权重相比较低,但你需要考虑该运算符的成本很大程度上取决于外部表大小和内部表大小的乘积。