SQLSERVER中的假脱机spool

SQLSERVER中的假脱机spool

我发现网上对于假脱机的解释都非常零散,究竟假脱机是什么?

这几天在家里研究了一下,收集了很多网上的资料

假脱机是中文的翻译,而英文的名字叫做 spool

 

在徐老师写的《SQLSERVER企业级平台管理实践》里提到了一下假脱机

在SQLSERVER I/O问题的那一节

在性能监视器里,有一个计数器“worktables/sec” :

每秒创建的工作表数。例如,工作表可用于存储查询假脱机(query spool),LOB变量,XML变量,表变量,游标的临时结果

在《剖析SQLServer执行计划》里也提到了假脱机

(13) 有时查询优化器需要在tempdb数据库中建立临时工作表。如果是这样的话

就意味着图形执行计划中有标识成Index Spool, Row Count Spool或者Table Spool的图标。

任何时候,使用到工作表一般都会防碍到性能,因为需要额外的I/O开销来维护这个工作表。

 

之前本人也写过一篇文章:对于索引假脱机的一点理解

写这篇文章的时候当时还是对假脱机一知半解

假脱机在MSDN中的执行计划中的逻辑运算符和物理运算符中提到了几个假脱机相关的运算符(详见本文最后面)

Eager Spool

Lazy Spool

Index Spool (有时候也叫 Nonclustered Index Spool)

Row Count Spool

Spool

Table Spool

Window Spool 

Spool, Table Spool, Index Spool, Window Spool 和 Row Count Spool是物理运算符

Eager Spool 和 Lazy Spool是逻辑运算符

 

这些运算符描述了假脱机是如何工作的,在这里你需要非常清楚逻辑运算符和物理运算符的区别

MSDN中的解释:

逻辑运算符:逻辑运算符描述了用于处理语句的关系代数操作。 换言之,逻辑运算符从概念上描述了需要执行哪些操作。

物理运算符:物理运算符实施由逻辑运算符描述的操作。 每个物理运算符都是一个执行某项具体操作的对象或例程。

例如,某些物理运算符可访问表、索引或视图中的列或行。 其他物理运算符执行其他操作,如计算、聚合、数据完整性检查或联接。

物理运算符具有与其关联的开销。

注意:窗口假脱机是没有Eager Spool和Lazy Spool之分的,因为他既是逻辑运算符也是物理运算符!!

 

简单来讲SQLSERVER做某项操作由物理运算符来做,而具体怎样做就由逻辑运算符来决定

打个比方:小明在佛山,想去广州,小明可以选择开汽车去广州,踩自行车去广州,骑摩托车去广州(相当于做某项操作)

小明可以根据当时的路况:

(1)踩自行车:如果道路比较拥堵,踩自行车不用怕,最多的车也能过,他可以选择使劲的踩(Eager Spool)或者慢慢踩(Lazy Spool)

(2)开汽车:如果道路比较畅通,他可以选择开快一点(Eager Spool)或者开慢一点(Lazy Spool)

(3)骑摩托车:如果道路比较拥堵,他可以选择抄小路,然后开快一点(Eager Spool)或者开慢一点(Lazy Spool)

 

不知道这个比喻大家明白没有,不过本人也找不到更好的比喻~

 

在图形执行计划中,你会发现Table Spool 有时候会带有  Eager Spool ,有时候有会带有 Lazy Spool

因为Table Spool是物理运算符,Eager Spool和Eager Spool 是逻辑运算符


Table Spool(表假脱机)

SQL脚本如下:

表假脱机 Eager Spool

 1 ----表假脱机 Eager Spool
 2 USE [Spool]
 3 GO
 4 CREATE TABLE Sales (EmpId INT, Yr INT, Sales MONEY)
 5 INSERT Sales VALUES(1, 2005, 12000)
 6 INSERT Sales VALUES(1, 2006, 18000)
 7 INSERT Sales VALUES(1, 2007, 25000)
 8 INSERT Sales VALUES(2, 2005, 15000)
 9 INSERT Sales VALUES(2, 2006, 6000)
10 INSERT Sales VALUES(3, 2006, 20000)
11 INSERT Sales VALUES(3, 2007, 24000)
12 
13 SELECT * FROM [dbo].[Sales]
14 
15 
16 SELECT EmpId, Yr, SUM(Sales) AS Sales
17 FROM Sales
18 GROUP BY EmpId, Yr WITH CUBE
View Code

例子出处:http://www.sqlskills.com/blogs/conor/grouping-sets-rollups-and-cubes-oh-my/

In this case, it writes the data to a temporary spool, sorts the output of that

and then re-reads that spool in the second branch. 

 

表假脱机 Lazy Spool

1 --表假脱机 Lazy Spool
2 USE [AdventureWorks]
3 GO
4 SELECT *,COUNT(*) OVER()
5 from production.[Product] AS p
6 JOIN production.[ProductSubcategory] AS s
7 ON s.[ProductCategoryID]=p.[ProductSubcategoryID]
View Code

例子出处:http://sqlblog.com/blogs/rob_farley/archive/2013/06/11/spooling-in-sql-execution-plans.aspx


Row Count Spool(行计数假脱机)

SQL脚本如下:

 1 --行计数假脱机
 2 USE [Spool]
 3 GO
 4 --建表
 5 CREATE TABLE tb1(ID int) 
 6 GO
 7 CREATE TABLE tb2(ID int) 
 8 GO
 9 
10 --插入测试数据
11 DECLARE @i INT 
12 SET @i= 500 
13 WHILE @i > 0 
14 begin 
15 INSERT INTO dbo.tb1 
16 VALUES ( @i 
17 ) 
18 SET @i = @i -1 
19 end 
20 GO
21 
22 DECLARE @i INT 
23 SET @i= 500 
24 WHILE @i > 0 
25 begin 
26 INSERT INTO dbo.tb2
27 VALUES ( @i 
28 ) 
29 SET @i = @i -1 
30 end 
31 
32 
33 --行计数假脱机
34 SELECT * FROM tb1 WHERE id NOT IN(SELECT id FROM tb2)
View Code

例子出处:http://niutuku.com/tech/MsSql/238716.shtml


Index Spool (索引假脱机)

Lazy Spool

SQL脚本如下:

 1 --索引假脱机(Index Spool)
 2 USE [Spool]
 3 GO
 4 --建表
 5 create  table   tb(aa   int,bb   char(1)) 
 6 GO  
 7 
 8 --插入测试数据
 9 insert   tb   values(1,'A')   
10 insert   tb   values(1,'B')   
11 insert   tb   values(1,'C')   
12 insert   tb   values(1,'D')   
13 
14 insert   tb   values(2,'E')   
15 insert   tb   values(2,'F')   
16 insert   tb   values(2,'G')   
17 insert   tb   values(2,'H')   
18 
19 insert   tb   values(3,'I')   
20 insert   tb   values(3,'J')   
21 insert   tb   values(3,'K')   
22 insert   tb   values(3,'L')
23   
24 --查询数据
25 SELECT  *
26 FROM    tb a
27 WHERE   bb = ( SELECT TOP 1
28                         bb
29                FROM     tb
30                WHERE    aa = a.aa
31                ORDER BY NEWID()
32              )
View Code

例子出处:http://www.cnblogs.com/lyhabc/archive/2013/04/19/3029840.html


Window Spool  (窗口假脱机)

Window Spool 这个执行计划和OVER() 开窗函数息息相关,因为只有OVER()函数才会使用到Window Spool 这个执行计划

http://msdn.microsoft.com/zh-cn/library/ms189461.aspx

大家可以看一下MSDN中对OVER()开窗函数里ROWS选项RANGE选项的解释

ROWS | RANGE
通过指定分区中的起点和终点,进一步限制分区中的行数。 这是通过按照逻辑关联或物理关联对当前行指定某一范围的行实现的。

物理关联通过使用 ROWS 子句实现。

ROWS 子句通过指定当前行之前或之后的固定数目的行,限制分区中的行数。

此外,RANGE 子句通过指定针对当前行中的值的某一范围的值,从逻辑上限制分区中的行数。

基于 ORDER BY 子句中的顺序对之前和之后的行进行定义。

窗口框架“RANGE … CURRENT ROW …”包括在 ORDER BY 表达式中与当前行具有相同值的所有行。

例如,ROWS BETWEEN 2 PRECEDING AND CURRENT ROW 意味着该函数对其操作的行的窗口在大小上是 3 行,以当前行之前(包括当前行)的 2 行开头。

SQL脚本如下:

 1 use master
 2 GO
 3 
 4 --range
 5 select count(*) over (order by id RANGE between current row and unbounded following)
 6 from   sysobjects
 7 order by id
 8 
 9 --rows
10 select count(*) over (order by type ROWS current row )
11 from   sysobjects
12 order by id
View Code

例子出处:http://www.sqlteam.com/forums/topic.asp?TOPIC_ID=182542


对上面这些运算符的解释:

假脱机运算符会取出表中的一部分的数据集,将他们存放在tempdb数据库里的临时结构里

这个临时结构一般就是堆表或者非聚集索引,但是有一个物理运算符例外,临时结构是不存放数据的,

他只存放假脱机里保存的数据的行数,而这个物理运算符就是Row Count spool

 

Index Spool:索引假脱机只有非聚集索引假脱机,没有聚集索引假脱机,结合我以前写的两篇文章,解释一下原因

SQLSERVER聚集索引与非聚集索引的再次研究(上)

SQLSERVER聚集索引与非聚集索引的再次研究(下)

SQLSERVER当遇到复杂的查询的时候,需要把部分结果集放到tempdb数据库里的非聚集索引页里(说白了就是在tempdb数据库里建立

表的非聚集索引)以加快查找的速度的时候就会用到索引假脱机

例如上面的例子,SQL语句用到了子查询(tb表),SQLSERVER需要把子查询里的结果集(tb表)进行排序然后将结果集放进去

非聚集索引里(对tb表建立非聚集索引),

然后用非聚集索引里的数据和主表(tb a)里的数据进行联接,并输出结果

为什麽不用聚集索引?

SQLSERVER聚集索引与非聚集索引的再次研究(上/下)里说到,非聚集索引和堆表是没有连接在一起的,非聚集索引页面只有指针

指向堆表的数据页,而聚集索引的叶子节点就是数据页,索引页和数据页连接在一起,如果建立聚集索引,就需要将表(tb表)中的数据

放入到tempdb数据库里,这样开销就会很大

或者用下面两张图来描述可能会清楚一点,关键还是要读懂 SQLSERVER聚集索引与非聚集索引的再次研究(上/下)

Table Spool:把表中的数据放进tempdb数据库里

为什麽第一个查询会用到Table Spool?因为CUBE这个数据汇总关键字会将表中的数据进行汇总,汇总的过程比较复杂

把表中的数据放进去tempdb数据库里的工作表(worktable、临时表、堆表)里进行复杂的汇总计算是比较好的

他避免了阻塞,以防止长期锁住表中的数据

关于CUBE关键字可以看一下我这篇文章:SQLSERVER中的ALL、PERCENT、CUBE关键字、ROLLUP关键字和GROUPING函数

 

Row Count Spool:存放中间结果/表的数据的行数,上面的例子里用于计算表中的数据行数并保存在tempdb数据库的

Row Count Spool里,为后面两表联接选用执行计划提供选择依据

 

 

 

Eager Spool逻辑运算符:一次性将所有数据放入到Spool里

Lazy Spool逻辑运算符:逐次逐次地将数据放入Spool里

在上面的例子里

Tabel Spool Eager Spool

SQLSERVER使用Eager Spool一次性将Sales 表中的数据存放到tempdb数据库的工作表里面,方便快速统计

Row Count Spool

SQLSERVER使用计数器每次读取到一行就加1,这样一次一次地统计表中的行数(这里只是比喻,SQLSERVER内部可能并不是这样统计!)

 

Window Spools:根据MSDN中的定义,OVER 子句定义查询结果集内的窗口或用户指定的行集。 然后,开窗函数将计算窗口中每一行的值

SQLSERVER将窗口中的结果集放入Spool里,以加快后续操作的速度

对于单独一个窗口来讲:单独一个窗口属于Eager Spool(一次性将结果集放进去窗口里)  

对于表中的窗口来讲:属于Lazy Spool ,因为每个窗口把数据存放进去窗口里的速度/顺序不是一致的,逐次逐次地将数据存放进去每个窗口


为什麽需要假脱机?

主要有两个原因:

1:数据需要再次被调用

2:使假脱机数据与源数据保持隔离

第二个原因很容易理解,就像第一个例子中的Tabel Spool那样,需要把表数据放进Tabel Spool里,以方便进行数据汇总,

而不影响原表数据

 

第一个原因可以再举一个例子

公用表表达式(CTE)

 1 USE [AdventureWorks]
 2 GO
 3 WITH managers AS(
 4 SELECT [EmployeeID],[ManagerID]
 5 from [HumanResources].[Employee]
 6 WHERE [ManagerID] IS NULL
 7 UNION ALL
 8 SELECT e.[EmployeeID],e.[ManagerID]
 9 from [managers] m
10 JOIN [HumanResources].[Employee] e
11 ON e.[ManagerID]=m.[EmployeeID]
12 )
13 
14 SELECT * FROM [managers]

 

索引假脱机运算符负责把数据一条一条地塞进去tempdb的非聚集索引里,并且是Lazy的,为什麽是Lazy的?

因为刚开始的时候只有一行记录,后来慢慢一条一条数据地从最右边的表假脱机里获取数据

我们还是先分析一下整个执行计划以方便理解,我们可以将整个执行计划拆解为三部分

第一部分 执行计划的右上角

1 SELECT [EmployeeID],[ManagerID]
2 from [HumanResources].[Employee]
3 WHERE [ManagerID] IS NULL

 

 这部分的执行计划只查找到一条记录

他把这条记录放入索引假脱机里

第二部分 UNION ALL

将第一部分的结果和第三部分的结果合并在一起

第三部分 执行计划的右下角

1 SELECT e.[EmployeeID],e.[ManagerID]
2 from [managers] m
3 JOIN [HumanResources].[Employee] e
4 ON e.[ManagerID]=m.[EmployeeID]

 

 

 

最右边的表假脱机运算符负责把表数据装载入表假脱机里,这个装载过程也是逐条数据装载的

那么,执行计划里的表假脱机和索引假脱机主要有什么用???

表假脱机主要用作公用表表达式里的递归调用

 1 WITH managers AS(
 2 SELECT [EmployeeID],[ManagerID]
 3 from [HumanResources].[Employee]
 4 WHERE [ManagerID] IS NULL
 5 UNION ALL
 6 SELECT e.[EmployeeID],e.[ManagerID]
 7 from [managers] m
 8 JOIN [HumanResources].[Employee] e
 9 ON e.[ManagerID]=m.[EmployeeID]
10 )

SELECT e.[EmployeeID],e.[ManagerID]
from [managers] m
JOIN [HumanResources].[Employee] e
ON e.[ManagerID]=m.[EmployeeID]

上面的代码是每次递归的时候都需要调用到的,所以SQLSERVER干脆把表数据放到假脱机里的,不用每次都去查找记录了

而索引假脱机是方便外部代码调用公用表表达式的时候不用每次都去计算公用表表达式的结果,直接把公用表表达式的结果

放进去索引假脱机,当SELECT * FROM managers的时候,直接到索引假脱机里取数据就可以了

1 SELECT * FROM [managers]

 

 

 

断定运算符在这里的作用是判断是否超过系统循环次数造成死循环,如果我们加上OPTION (MAXRECURSION 0)

断定运算符就会消失

 1 USE [AdventureWorks]
 2 GO
 3 WITH managers AS(
 4 SELECT [EmployeeID],[ManagerID]
 5 from [HumanResources].[Employee]
 6 WHERE [ManagerID] IS NULL
 7 UNION ALL
 8 SELECT e.[EmployeeID],e.[ManagerID]
 9 from [managers] m
10 JOIN [HumanResources].[Employee] e
11 ON e.[ManagerID]=m.[EmployeeID]
12 )
13 
14 SELECT * FROM [managers] OPTION (MAXRECURSION 0)


万圣节问题 

网上有两篇文章介绍了这个问题

园子里的这篇文章介绍非常不深入,看了之后还是不明白

http://www.cnblogs.com/xwdreamer/archive/2012/05/28/2522404.html

simple-talk网站的文章就介绍得非常清晰

https://www.simple-talk.com/sql/learn-sql-server/operator-of-the-week---spools,-eager-spool/

在介绍之前先来做一个小实验

下面SQL脚本建立一个非聚集索引表,并且非聚集索引的第一个字段是salary 并且按salary升序排序!!!

 1 USE [Spool]
 2 GO
 3 
 4 CREATE TABLE nct(id INT IDENTITY(1,1),NAME VARCHAR(30), salary INT);
 5 GO
 6 --建立非聚集索引  切记:非聚集索引的第一个字段是salary 并且按salary升序排序!!!
 7 CREATE  INDEX ix_nct ON nct(salary ASC,[ID],[NAME]) 
 8 GO
 9  
10 --插入数据
11 INSERT INTO [dbo].[nct] ( [NAME],[salary] )
12 SELECT '小明', 1 UNION ALL
13 SELECT '小华', 2 UNION ALL
14 SELECT '小芳', 3
15 GO
16 
17 SELECT * FROM [dbo].[nct]
View Code

我们看一下非聚集索引页

 1 CREATE TABLE DBCCResult (
 2 PageFID NVARCHAR(200),
 3 PagePID NVARCHAR(200),
 4 IAMFID NVARCHAR(200),
 5 IAMPID NVARCHAR(200),
 6 ObjectID NVARCHAR(200),
 7 IndexID NVARCHAR(200),
 8 PartitionNumber NVARCHAR(200),
 9 PartitionID NVARCHAR(200),
10 iam_chain_type NVARCHAR(200),
11 PageType NVARCHAR(200),
12 IndexLevel NVARCHAR(200),
13 NextPageFID NVARCHAR(200),
14 NextPagePID NVARCHAR(200),
15 PrevPageFID NVARCHAR(200),
16 PrevPagePID NVARCHAR(200)
17 )
18 
19 --TRUNCATE TABLE [dbo].[DBCCResult]
20 
21 INSERT INTO DBCCResult EXEC ('DBCC IND(Spool,nct,-1) ')
22 
23 SELECT * FROM [dbo].[DBCCResult] ORDER BY [PageType] DESC 
24 
25 DBCC TRACEON(3604,-1)
26 GO
27 DBCC PAGE(Spool,1,47,3) 
28 GO
View Code

非聚集索引按照Salary字段升序排序

我们用SQL语句update一下小华的Salary

1 UPDATE nct SET Salary = 4
2 WHERE [NAME]='小华'

这里是按照非聚集索引的Range Scan读取出结果的:SQLSERVER中的ALLOCATION SCAN和RANGE SCAN

再看一下非聚集索引页面

我们看一下update前和update后非聚集索引页面的变化

可以看到,update之后非聚集索引马上根据非聚集索引键(Salary字段)重新进行升序排序

--------------------------------------------------------------------------------------------

使用下面SQL脚本建立测试环境

 1 USE [Spool]
 2 GO
 3 
 4 
 5 --建表
 6 CREATE TABLE Halloween
 7 (
 8   ID INT IDENTITY(1, 1)
 9          PRIMARY KEY ,
10   Name VARCHAR(30) ,
11   Salary NUMERIC(18, 2),
12   Remark NVARCHAR(3000)
13 )
14 GO
15  
16 --插入数据
17 INSERT INTO [dbo].[Halloween] ( [Name], [Salary], [Remark] )
18 SELECT '小明',1,replicate('a', 3000) UNION ALL
19 SELECT '小方',2,replicate('a', 3000) 
20 
21 
22 
23 
24 --建立非聚集索引
25 CREATE NONCLUSTERED INDEX ix_Halloween ON Halloween(Salary ASC)
26 GO
27 
28 --查询
29 SELECT * FROM Halloween
30 GO
View Code

我们用下面SQL语句看一下聚集索引页面和非聚集索引页面

 1 CREATE TABLE DBCCResult (
 2 PageFID NVARCHAR(200),
 3 PagePID NVARCHAR(200),
 4 IAMFID NVARCHAR(200),
 5 IAMPID NVARCHAR(200),
 6 ObjectID NVARCHAR(200),
 7 IndexID NVARCHAR(200),
 8 PartitionNumber NVARCHAR(200),
 9 PartitionID NVARCHAR(200),
10 iam_chain_type NVARCHAR(200),
11 PageType NVARCHAR(200),
12 IndexLevel NVARCHAR(200),
13 NextPageFID NVARCHAR(200),
14 NextPagePID NVARCHAR(200),
15 PrevPageFID NVARCHAR(200),
16 PrevPagePID NVARCHAR(200)
17 )
18 
19 --TRUNCATE TABLE [dbo].[DBCCResult]
20 INSERT INTO DBCCResult EXEC ('DBCC IND(spool,Halloween,-1) ')
21 
22 SELECT * FROM [dbo].[DBCCResult] ORDER BY [PageType] DESC 
23 
24 
25 DBCC TRACEON(3604,-1)
26 GO
27 DBCC PAGE(spool,1,184,3) 
28 GO
29 DBCC PAGE(spool,1,93,3) 
30 GO
View Code

聚集索引页面

非聚集索引页面

我们update一下Salary等于1的那位员工的工资

1 UPDATE Halloween SET Salary = 2.5
2 FROM Halloween 
3 WHERE Salary =1

再看一下聚集索引页面和非聚集索引页面

聚集索引页面

非聚集索引页面

非聚集索引马上按照非聚集索引键(Salary字段)进行重新排序

这里似乎没有什么问题,我们drop掉Halloween表,并重新建立测试环境

 1 USE [Spool]
 2 GO
 3 
 4 
 5 
 6 --建表
 7 CREATE TABLE Halloween
 8 (
 9   ID INT IDENTITY(1, 1)
10          PRIMARY KEY ,
11   Name VARCHAR(30) ,
12   Salary NUMERIC(18, 2),
13   Remark NVARCHAR(3000)
14 )
15 GO
16  
17 --插入数据
18 INSERT INTO [dbo].[Halloween] ( [Name], [Salary], [Remark] )
19 SELECT '小明',1,replicate('a', 3000) UNION ALL
20 SELECT '小方',2,replicate('a', 3000) 
21 
22 
23 
24 
25 --建立非聚集索引
26 CREATE NONCLUSTERED INDEX ix_Halloween ON Halloween(Salary ASC)
27 GO
28 
29 --查询
30 SELECT * FROM Halloween
31 GO
32 
33 
34 
35 
36 
37 CREATE TABLE DBCCResult (
38 PageFID NVARCHAR(200),
39 PagePID NVARCHAR(200),
40 IAMFID NVARCHAR(200),
41 IAMPID NVARCHAR(200),
42 ObjectID NVARCHAR(200),
43 IndexID NVARCHAR(200),
44 PartitionNumber NVARCHAR(200),
45 PartitionID NVARCHAR(200),
46 iam_chain_type NVARCHAR(200),
47 PageType NVARCHAR(200),
48 IndexLevel NVARCHAR(200),
49 NextPageFID NVARCHAR(200),
50 NextPagePID NVARCHAR(200),
51 PrevPageFID NVARCHAR(200),
52 PrevPagePID NVARCHAR(200)
53 )
54 
55 --TRUNCATE TABLE [dbo].[DBCCResult]
56 INSERT INTO DBCCResult EXEC ('DBCC IND(spool,Halloween,-1) ')
57 
58 SELECT * FROM [dbo].[DBCCResult] ORDER BY [PageType] DESC 
59 
60 
61 DBCC TRACEON(3604,-1)
62 GO
63 DBCC PAGE(spool,1,184,3) 
64 GO
65 DBCC PAGE(spool,1,93,3) 
66 GO
View Code

这次我们使用下面update语句,记住一定要加WITH(INDEX=ix_Halloween)

1 USE [Spool]
2 GO
3 UPDATE Halloween SET Salary = [Salary]*2.5
4 FROM Halloween WITH(INDEX=ix_Halloween)
5 WHERE Salary <7

如果我们加了WITH(INDEX=ix_Halloween),SQLSERVER就会走非聚集索引查找

如果我们不加WITH(INDEX=ix_Halloween),SQLSERVER就会走聚集索引扫描

这里不讨论加不加WITH(INDEX=ix_Halloween)的问题

关键我们加WITH(INDEX=ix_Halloween)就是为了让SQLSERVER走非聚集索引

update了之后正常的结果应该是这样的

为什麽会这样?

还记得刚才我们说到了非聚集索引更新了之后马上进行排序吗?

用下面的图来表示应该会比较清楚

SQLSERVER使用Table Spool来解决万圣节问题

先将非聚集索引的数据放进去Table Spool(临时表)里,然后逐行逐行扫描临时表,这样就不会遇到非聚集索引更新后马上进行排序的问题了

使用Table Spool后就能够得到正确结果

为什麽不用Index Spool而用Table Spool?

之前我们说过Index Spool在tempdb数据库里建立临时的非聚集索引,把非聚集索引里的数据

放进去非聚集索引里,那不是会继续遇到万圣节问题???

下面这个SQL语句也是使用了Table Spool来避免万圣节问题

1 USE [AdventureWorks]
2 GO
3 UPDATE  s
4 SET     [Name] = 'Z' + [Name]
5 FROM    Production.ProductSubcategory AS s WITH ( INDEX ( [AK_ProductSubcategory_Name] ) )
6 WHERE   [Name] >= 'N'

 

 

万圣节问题

update数据的时候,如果update的是非聚集索引的第一个字段(即非聚集索引键)的时候并且走的是非聚集索引扫描/查找
都有可能引起万圣节问题
SQLSERVER的解决方法是把非聚集索引里的数据全部移到Tabel Spool(Eager)里
防止由于更新非聚集索引的非聚集索引键而引起的非聚集索引重新排序,造成数据更新错误的问题

 

然后,其实Index Spool又好Table Spool又好,都是属于临时表的一种

 

类似案例
https://time.geekbang.org/column/article/80801
insert into t(c,d) (select c+1, d from t force index(c) order by c desc limit 1);
至于这个语句的执行为什么需要临时表,原因是这类一边遍历数据,一边更新数据的情况,如果读出来的数据直接写回原表,
就可能在遍历过程中,读到刚刚插入的记录,新插入的记录如果参与计算逻辑,就跟语义不符。

 

--建立非聚集索引
CREATE NONCLUSTERED INDEX ix_Halloween ON Halloween(Salary ASC)
GO
UPDATE Halloween SET Salary = [Salary]*2.5
FROM Halloween WITH(INDEX=ix_Halloween)
WHERE Salary <7

这类场景,其实都可以归纳为一边遍历数据,一边更新数据的情况,通用解决方案都是使用临时表,先暂存现有的表数据,然后对临时表的数据进行计算,计算完之后再更新回去原表


总结

 

实际上这些假脱机运算符的本质跟临时表和表变量是一样的,都是以空间换时间,以达到性能上的平衡!

文章最后面附上MSDN里的SQLSERVER所有的执行计划(逻辑运算符和物理运算符)

参考文章:

http://www.scarydba.com/2009/09/09/spools-in-execution-plans/

https://www.simple-talk.com/sql/learn-sql-server/operator-of-the-week---spools,-eager-spool/

http://sqlblog.com/blogs/rob_farley/archive/2013/06/11/spooling-in-sql-execution-plans.aspx

 

如有不对的地方,欢迎大家拍砖o(∩_∩)o 



Showplan 逻辑运算符和物理运算符参考

本节介绍了各个逻辑运算符和物理运算符。

 

图形执行计划图标

Showplan 运算符

说明

Aggregate

Aggregate 运算符计算包含 MIN、MAX、SUM、COUNT 或 AVG 的表达式。 Aggregate 既是一个逻辑运算符,也是一个物理运算符。

Arithmetic Expression 运算符图标

Arithmetic Expression

Arithmetic Expression 运算符根据行中的现有值计算新值。 SQL Server 2012 中不使用 Arithmetic Expression

Assert 运算符图标

Assert

Assert 运算符用于验证条件。 例如,验证引用完整性或确保标量子查询返回一行。 对于每个输入行,Assert 运算符都要计算执行计划的Argument 列中的表达式。 如果此表达式的值为 NULL,则通过 Assert 运算符传递该行,并且查询执行将继续。 如果此表达式的值非 Null,则将产生相应的错误。 Assert 运算符是一个物理运算符。

Assign 语言元素图标

Assign

Assign 运算符将表达式的值或常量分配给变量。 Assign 是一个语言元素。

Asnyc Concat

Asnyc Concat 运算符仅用于远程查询(分布式查询)。 它有 n 个子节点和一个父节点。 通常,某些子节点是参与分布式查询的远程计算机。 Asnyc Concat 同时向所有子节点发出 open() 调用,然后将位图应用于每个子节点。 对于为 1 的每个位,Async Concat 按需向父节点发送输出行。

Bitmap 运算符图标

Bitmap

SQL Server 使用 Bitmap 运算符来实现并行查询计划中的位图筛选。 在将行传递给另一个运算符(如 Parallelism 运算符)之前,通过消除无法生成任何联接记录的键值的行,位图筛选可提高查询的执行速度。 位图筛选器使用运算符树某部分的表中一组值的简洁表示形式来筛选位于该树另一部分的第二张表中的行。 通过在查询中预先删除不必要的行,后续运算符将处理较少的行,从而提高查询的整体性能。优化器将确定位图的选择性何时可满足使用条件以及在哪些运算符上应用筛选器。 Bitmap 是一个物理运算符。

Bitmap 运算符图标

Bitmap Create

Bitmap Create 运算符出现在创建位图的显示计划输出中。 Bitmap Create 是一个逻辑运算符。

Bookmark Lookup 运算符图标

Bookmark Lookup

Bookmark Lookup 运算符使用书签(行 ID 或聚集键)在表或聚集索引内查找相应的行。 Argument 列包含书签标签,用于在表或聚集索引内查找行。 Argument 列还包含要查找的行所在的表或聚集索引的名称。 如果 Argument 列中出现 WITH PREFETCH 子句,则表示查询处理器已决定在表或聚集索引内查找书签时将使用异步预提取(预读)作为最佳选择。

SQL Server 2012 中不使用 Bookmark Lookup 而由 Clustered Index Seek  RID Lookup 提供书签查找功能。 Key Lookup 运算符也提供此功能。

Branch Repartition

在并行查询计划中,有时存在迭代器的概念性区域。 此类区域中的所有迭代器都可通过并行线程执行。 这些区域本身必须串行执行。 单个区域内的某些 Parallelism 迭代器称为 Branch Repartition 两个这样的区域边界上的 Parallelism 迭代器称为 Segment RepartitionBranch Repartition  Segment Repartition 是逻辑运算符。

Broadcast

Broadcast 有一个子节点和 n 个父节点。 Broadcast 根据使用者的请求将其输入行发送给多个使用者。 每个使用者都将获得所有行。 例如,如果所有使用者都是哈希联接的生成端,则将生成 n 份哈希表。

Build hash 运算符图标

Build Hash

指示为 xVelocity 内存优化的列存储索引生成批处理哈希表。

Cache

Cache 是一个专门的 Spool 运算符。 它仅存储一行数据。 Cache 是一个逻辑运算符。 SQL Server 2012 中不使用 Cache

Clustered Index Delete 运算符图标

Clustered Index Delete

Clustered Index Delete 运算符可删除查询执行计划的 Argument 列指定的群集索引中的行。 如果 Argument 列中存在 WHERE:() 谓词,则仅删除满足该谓词要求的行。Clustered Index Delete 是一个物理运算符。

Clustered Index Insert 运算符图标

Clustered Index Insert

Clustered Index Insert Showplan 运算符可将其输入中的行插入在 Argument 列指定的聚集索引中。 Argument 列还包含一个 SET:() 谓词,用于指示为每一列设置的值。 如果 Clustered Index Insert 的插入值没有子项,则插入的行来自 Insert 运算符本身。Clustered Index Insert 是一个物理运算符。

Clustered Index Merge 运算符

Clustered Index Merge

Clustered Index Merge 运算符可将合并数据流应用于聚集索引。 该运算符可在其 Argument 列中所指定的聚集索引中删除、更新或插入行。 执行的实际操作取决于该运算符的 Argument 列中指定的 ACTION 列的运行时值。 Clustered Index Merge 是一个物理运算符。

Clustered Index Scan 运算符图标

Clustered Index Scan

Clustered Index Scan 运算符会扫描查询执行计划的 Argument 列中指定的聚集索引。 存在可选 WHERE:() 谓词时,则只返回满足该谓词的那些行。 如果 Argument 列包含 ORDERED 子句,则查询处理器已请求按聚集索引排列行的顺序返回行输出。 如果没有 ORDERED 子句,存储引擎将以最佳方式扫描索引,而无需对输出进行排序。 Clustered Index Scan 既是一个逻辑运算符,也是一个物理运算符。

Clustered Index Seek 运算符图标

Clustered Index Seek

Clustered Index Seek 运算符可以利用索引的查找功能从聚集索引中检索行。 Argument 列包含所使用的聚集索引名称和 SEEK:() 谓词。存储引擎仅使用索引来处理满足此 SEEK:() 谓词的行。 它还包括 WHERE:() 谓词,其中存储引擎对满足 SEEK:() 谓词的所有行进行计算,但此操作是可选的,并且不使用索引来完成此过程。

如果 Argument 列包含 ORDERED 子句,则表示查询处理器已决定必须按聚集索引排序行的顺序返回行。 如果没有 ORDERED 子句,存储引擎将以最佳方式搜索索引,而不对输出进行必要的排序。 若允许输出保持顺序,则效率可能比生成非排序输出的效率低。 出现关键字 LOOKUP 时,将执行书签查找。 在 SQL Server 2008 和更高版本中,Key Lookup 运算符提供书签查找功能。 Clustered Index Seek 既是一个逻辑运算符,也是一个物理运算符。

Clustered Index Update 运算符图标

Clustered Index Update

Clustered Index Update 运算符更新 Argument 列指定的聚集索引中的输入行。如果存在 WHERE:() 谓词,则只更新那些满足此谓词要求的行。 如果存在 SET:() 谓词,则将每个更新的列设置为该值。 如果存在 DEFINE:() 谓词,则列出此运算符定义的值。 可以在 SET 子句中、该运算符内的其他位置和该查询内的其他位置引用这些值。 Clustered Index Update 既是一个逻辑运算符,也是一个物理运算符。

Collapse 运算符图标

Collapse

Collapse 运算符用于优化更新处理。 执行更新时,可以将该更新操作拆分(使用 Split 运算符)成为删除和插入操作。 Argument 列包含一个指定键列列表的 GROUP BY:() 子句。 如果查询处理器遇到删除和插入相同键值的相邻行,则会用一个更有效的更新操作替换这些单独的操作。 Collapse 既是一个逻辑运算符,也是一个物理运算符。

Columnstore Index Scan

Columnstore Index Scan

Columnstore Index Scan 运算符会扫描查询执行计划的 Argument 列中指定的列存储索引。

Compute Scalar 运算符图标

Compute Scalar

Compute Scalar 运算符通过对表达式求值来生成计算标量值。 该值可以返回给用户、在查询中的其他位置引用或二者皆可。 例如,在筛选谓词或联接谓词中就会出现二者皆可的情况。 Compute Scalar 既是一个逻辑运算符,也是一个物理运算符。

在 SET STATISTICS XML 生成的显示计划中出现的 Compute Scalar 运算符可能不包含 RunTimeInformation 元素。 在图形显示计划中,当已在 SQL Server Management Studio 中选中“包括实际的执行计划”选项时,“实际行”“实际重新绑定次数”“实际重绕次数”可能不会出现在“属性”窗口中。 当出现这种情况时,意味着虽然编译过的查询计划中使用了这些运算符,但在运行时查询计划中,它们的作用是由其他运算符实现的。 另外,请注意,SET STATISTICS PROFILE 生成的显示计划输出中的执行数等于 SET STATISTICS XML 生成的显示计划中的重新绑定次数和重绕次数的总和。

Concatenation 运算符图标

Concatenation

Concatenation 运算符扫描多个输入,并返回每个扫描的行。 Concatenation 通常用于实现 Transact-SQL UNION ALL 结构。Concatenation 物理运算符有两个或多个输入,有一个输出。 Concatenation 将行从第一个输入流复制到输出流,然后对其他输入流重复进行此操作。 Concatenation 既是一个逻辑运算符,也是一个物理运算符。

Constant Scan 运算符图标

Constant Scan

Constant Scan 运算符可将一个或多个常量行引入到查询中。 Compute Scalar 运算符通常在 Constant Scan 之后使用,以将列添加到Constant Scan 运算符生成的行中。

Convert(数据库引擎)语言元素图标

Convert

Convert 运算符将标量数据类型转换为另一种类型。 Convert 是一个语言元素。

Cross Join

Cross Join 运算符将第一个(顶端)输入中的每一行与第二个(底端)输入中的每一行联接在一起。 Cross Join 是一个逻辑运算符。

Cursor catchall cursor 运算符图标

catchall

生成图形显示计划的逻辑找不到迭代器的合适图标时,将显示通用图标。 通用图标不一定指示存在错误。 有三种通用图标:蓝色(用于迭代器)、橙色(用于游标)和绿色(用于 Transact-SQL 语言元素)。

Cursor

Cursor 逻辑运算符和物理运算符用于描述涉及游标操作的查询或更新的执行方式。 其中物理运算符描述用于处理游标(如使用键集驱动游标)的物理实现算法。 游标执行过程的每一步都涉及物理运算符。 而逻辑运算符描述游标的属性,如游标是只读。

逻辑运算符包括 Asynchronous、Optimistic、Primary、Read Only、Scroll Locks、Secondary 和 Synchronous。

物理运算符包括 Dynamic、Fetch Query、Keyset、Population Query、Refresh Query 和 Snapshot。

Declare 语言元素图标

Declare

Declare 运算符用于分配查询计划中的局部变量。 Declare 是一个语言元素。

Delete(数据库引擎)运算符图标

Delete

Delete 运算符将从对象中删除满足 Argument 列内的可选谓词的行。

Delete Scan 运算符图标

Deleted Scan

Deleted Scan 运算符在触发器中扫描删除的表。

Distinct

Distinct 运算符可以从行集或值集中删除重复项。 Distinct 是一个逻辑运算符。

Distinct Sort

Distinct Sort 逻辑运算符将对输入进行扫描,删除重复项并按 Argument 列的 DISTINCT ORDER BY:() 谓词中指定的列进行排序。 Distinct Sort 是一个逻辑运算符。

Distribute Streams Parallelism 运算符图标

Distribute Streams

Distribute Streams 运算符仅用于并行查询计划。 Distribute Streams 运算符接收记录的单个输入流,并生成多个输出流。 记录的内容和格式不会改变。 输入流中的每个记录都将在某个输出流中显示。 此运算符在输出流中自动保留输入记录的相对顺序。 通常情况下,使用哈希操作确定特定输入记录所属的输出流。

如果将输出分区,那么 Argument 列会包含 PARTITION COLUMNS:() 谓词和分区列。 Distribute Streams 是一个逻辑运算符。

Dynamic Cursor 运算符图标

Dynamic

Dynamic 运算符使用可以查看其他游标所做的任何更改的游标。

Spool 运算符图标

Eager Spool

Eager Spool 运算符获取整个输入,并将每行存储在 tempdb 数据库中存储的隐藏临时对象中。 如果重绕该运算符(例如通过 Nested Loops 运算符重绕),但不需要任何重新绑定,则将使用假脱机数据,而不用重新扫描输入。 如果需要重新绑定,将丢弃假脱机数据,并通过重新扫描(重新绑定的)输入重新生成假脱机对象。 Eager Spool 运算符按“急切”方式生成自己的假脱机文件:当假脱机的父运算符请求第一行时,假脱机运算符将获取所有来自其输入运算符的行并将其存储在假脱机中。 Eager Spool 是一个逻辑运算符。

Fetch Query Cursor 运算符图标

Fetch Query

当对游标发出提取命令时,Fetch Query 运算符将检索行。

Filter(数据库引擎)运算符图标

Filter

Filter 运算符扫描输入,仅返回那些符合 Argument 列中的筛选表达式(谓词)的行。

Flow Distinct

Flow Distinct 逻辑运算符用于通过扫描输入来删除重复项。 虽然 Distinct 运算符在生成任何输入前使用所有的输入,但 FlowDistinct 运算符在从输入获得行时返回每行(除非该行是一个重复项,若是这样则删除该行)。

Full Outer Join

Full Outer Join 逻辑运算符从第一个(顶端)输入中与第二个(底端)输入相联接的行中返回每个满足联接谓词的行。 它还可以从下列输入返回行:

  • 在第二个输入中没有匹配项的第一个输入。

  • 在第一个输入中没有匹配项的第二个输入。

不包含匹配值的输入将作为空值返回。 Full Outer Join 是一个逻辑运算符。

Gather Streams Parallelism 运算符图标

Gather Streams

Gather Streams 运算符仅用在并行查询计划中。 Gather Streams 运算符处理几个输入流并通过组合这几个输入流生成单个记录输出流。不更改记录的内容和格式。 如果此运算符保留顺序,则所有的输入流都必须有序。 如果输出已排序,则 Argument 列包含一个 ORDER BY:() 谓词和正在排序的列名称。 Gather Streams 是一个逻辑运算符。

Hash Match 运算符图标

Hash Match

Hash Match 运算符通过计算其生成输入中每行的哈希值生成哈希表。 HASH:() 谓词以及一个用于创建哈希值的列的列表出现在 Argument列内。 然后,该谓词为每个探测行(如果适用)计算哈希值(使用相同的哈希函数)并在哈希表内查找匹配项。 如果存在残留谓词(由Argument 列中的 RESIDUAL:() 标识),则还须满足此残留谓词,只有这样行才能被视为是匹配项。 行为取决于所执行的逻辑操作:

  • 对于联接,使用第一个(顶端)输入生成哈希表,使用第二个(底端)输入探测哈希表。 按联接类型规定的模式输出匹配项(或不匹配项)。 如果多个联接使用相同的联接列,这些操作将分组为一个哈希组。

  • 对于非重复或聚合运算符,使用输入生成哈希表(删除重复项并计算聚合表达式)。 生成哈希表时,扫描该表并输出所有项。

  • 对于 union 运算符,使用第一个输入生成哈希表(删除重复项)。 使用第二个输入(它必须没有重复项)探测哈希表,返回所有没有匹配项的行,然后扫描该哈希表并返回所有项。

Hash Match 是一个物理运算符。

If 语言元素图标

If

If 运算符执行基于表达式的有条件处理。 If 是一个语言元素。

Inner Join

Inner Join 逻辑运算符返回满足第一个(顶端)输入与第二个(底端)输入所组成的联接的每一行。

Insert(数据库引擎)运算符图标

Insert

Insert 逻辑运算符将每行从其输入插入 Argument 列内指定的对象中。 相应的物理运算符为 Table InsertIndex Insert  Clustered Index Insert 运算符。

Inserted Scan 运算符图标

Inserted Scan

Inserted Scan 运算符扫描插入的表。 Inserted Scan 既是一个逻辑运算符,也是一个物理运算符。

Intrinsic 语言元素图标

Intrinsic

Intrinsic 运算符调用内部 Transact-SQL 函数。 Intrinsic 是一个语言元素。

Iterator Catchall 运算符图标

Iterator

生成图形显示计划的逻辑找不到 Iterator 的合适图标时,将显示通用图标。 通用图标不一定指示存在错误。 有三种通用图标:蓝色(用于迭代器)、橙色(用于游标)和绿色(用于 Transact-SQL 语言构造)。

Bookmark Lookup 运算符图标

Key Lookup

Key Lookup 运算符是在具有聚集索引的表上进行的书签查找。 Argument 列包含聚集索引的名称和用来在聚集索引中查找行的聚集键。Key Lookup 通常带有 Nested Loops 运算符。 如果 Argument 列中出现 WITH PREFETCH 子句,则表示查询处理器已决定在聚集索引内查找书签时将使用异步预提取(预读)作为最佳选择。

在查询计划中使用 Key Lookup 运算符表明该查询可能会从性能优化中获益。 例如,添加涵盖索引可能会提高查询性能。

Keyset Cursor 运算符图标

Keyset

Keyset 运算符使用的游标可用于查看其他用户所做的更新,而不能查看其他用户所做的插入。

语言元素通用图标

Language Element

生成图形显示计划的逻辑找不到 Language Element 的合适图标时,将显示通用图标。 通用图标不一定指示存在错误。 有三种通用图标:蓝色(用于迭代器)、橙色(用于游标)和绿色(用于 Transact-SQL 语言构造)。

Spool 运算符图标

Lazy Spool

Lazy Spool 逻辑运算符将其输入中的每一行存储到 tempdb 数据库内存储的隐藏临时对象中。 如果重绕该运算符(例如通过 Nested Loops 运算符重绕),但不需要任何重新绑定,则将使用假脱机数据,而不用重新扫描输入。 如果需要重新绑定,则将放弃假脱机数据,并通过重新扫描(重新绑定的)输入重新生成假脱机对象。 Lazy Spool 运算符以“迟缓”方式生成其假脱机文件,即每当假脱机父运算符请求一行时,假脱机运算符便从其输入运算符获取一行,然后将该行存储在假脱机中,而不是一次处理所有行。 Lazy Spool 是一个逻辑运算符。

Left Anti Semi Join

当第二个(底端)输入中没有匹配行时,Left Anti Semi Join 运算符返回第一个(顶端)输入中的每一行。 如果 Argument 列内不存在任何联接谓词,则每行都是一个匹配行。 Left Anti Semi Join 是一个逻辑运算符。

Left Outer Join

Left Outer Join 运算符返回满足第一个(顶端)输入与第二个(底端)输入联接的每一行。 它还返回任何在第二个输入中没有匹配行的第一个输入中的行。 第二个输入中的非匹配行作为空值返回。 如果 Argument 列内不存在任何联接谓词,则每行都是一个匹配行。 Left Outer Join 是一个逻辑运算符。

Left Semi Join

当第二个(底端)输入中有匹配行时,Left Semi Join 运算符返回第一个(顶端)输入中的每行。 如果 Argument 列内不存在任何联接谓词,则每行都是一个匹配行。 Left Semi Join 是一个逻辑运算符。

Log Row Scan 运算符图标

Log Row Scan

Log Row Scan 运算符用于扫描事务日志。 Log Row Scan 既是一个逻辑运算符,也是一个物理运算符。

Merge Interval 运算符图标

Merge Interval

Merge Interval 运算符可合并多个(可能重叠的)间隔以得出最小的不重叠间隔,然后将其用于查找索引项。 此运算符通常出现在Constant Scan 运算符中的一个或多个 Compute Scalar 运算符上方,后者运算符构造了此运算符所合并的间隔(表示为一行中的多个列)。Merge Interval 既是一个逻辑运算符,也是一个物理运算符。

Merge Join 运算符图标

Merge Join

Merge Join 运算符执行内部联接、左外部联接、左半部联接、左反半部联接、右外部联接、右半部联接、右反半部联接和联合逻辑运算。

 Argument 列中,如果操作执行一对多联接,则 Merge Join 运算符将包含 MERGE:() 谓词;如果操作执行多对多联接,则该运算符将包含 MANY-TO-MANY MERGE:() 谓词。 Argument 列还包含一个用于执行操作的列的列表,该列表以逗号分隔。 Merge Join 运算符要求在各自的列上对两个输入进行排序,这可以通过在查询计划中插入显式排序操作来实现。 如果不需要显式排序(例如,如果数据库内有合适的 B 树索引或可以对多个操作(如合并联接和对汇总分组)使用排序顺序),则合并联接尤其有效。 Merge Join 是一个物理运算符。

Nested Loops 运算符图标

Nested Loops

Nested Loops 运算符执行内部联接、左外部联接、左半部联接和左反半部联接逻辑运算。 Nested Loops 联接通常使用索引在内部表中搜索外部表的每一行。根据预计的开销,查询处理器决定是否对外部输入进行排序来改变内部输入索引的搜索位置。 将基于所执行的逻辑操作返回所有满足 Argument 列内的(可选)谓词的行。 Nested Loops 是一个物理运算符。

Nonclustered Index Delete 运算符图标

Nonclustered Index Delete

Nonclustered Index Delete 运算符通过 Argument 列中指定的非聚集索引删除输入行。 Nonclustered Index Delete 是一个物理运算符。

Nonclustered Index Insert 运算符图标

Index Insert

Index Insert 运算符用于将行从其输入插入到 Argument 列中指定的非聚集索引中。 Argument 列还包含一个 SET:() 谓词,用于指示为每一列设置的值。 Index Insert 是一个物理运算符。

Nonclustered Index Scan 运算符图标

Index Scan

Index Scan 运算符从 Argument 列中指定的非聚集索引中检索所有行。 如果可选的 WHERE:() 谓词出现在 Argument 列中,则仅返回满足此谓词的那些行。 Index Scan 既是一个逻辑运算符,也是一个物理运算符。

Nonclustered Index Seek 运算符图标

Index Seek

Index Seek 运算符利用索引的查找功能从非聚集索引中检索行。 Argument 列包含所使用的非聚集索引的名称。 它还包括 SEEK:() 谓词。存储引擎仅使用索引来处理满足 SEEK:() 谓词的行。 它可能还包含一个 WHERE:() 谓词,其中存储引擎对满足 SEEK:() 谓词的所有行进行计算(不使用索引来完成)。 如果 Argument 列包含 ORDERED 子句,则表示查询处理器已决定必须按非聚集索引排序行的顺序返回行。 如果没有 ORDERED 子句,则存储引擎将以最佳方式(不保证对输出排序)搜索索引。 如果让输出保持其顺序,则效率可能低于生成非排序输出。 Index Seek 既是一个逻辑运算符,也是一个物理运算符。

Nonclustered Index Spool 运算符图标

Index Spool

Index Spool 物理运算符在 Argument 列中包含 SEEK:() 谓词。 Index Spool 运算符扫描其输入行,将每行的副本放置在隐藏的假脱机文件(存储在 tempdb 数据库中且只在查询的生存期内存在)中,并为这些行创建非聚集索引。 这样可以使用索引的查找功能来仅输出那些满足 SEEK:() 谓词的行。 如果重绕该运算符(例如通过 Nested Loops 运算符重绕),但不需要任何重新绑定,则将使用假脱机数据,而不用重新扫描输入。

Nonclustered Index Update 运算符图标

Nonclustered Index Update

Nonclustered Index Update 物理运算符用于更新 Argument 列内指定的非聚集索引中的输入行。 如果存在 SET:() 谓词,则将每个更新的列设置为该值。 Nonclustered Index Update 是一个物理运算符。

Online Index Insert 运算符图标

Online Index Insert

Online Index Insert 物理运算符指示索引创建、更改或删除操作是在线执行的。 也就是说,基础表数据在索引操作期间仍然对用户可用。

Parallelism

Parallelism 运算符执行分发流、收集流和对流重新分区逻辑操作。 Argument 列可以包含一个 PARTITION COLUMNS:() 谓词和一个以逗号分隔的分区列的列表。 Argument 列还可以包含一个 ORDER BY:() 谓词,以列出分区过程中要保留排序顺序的列。 Parallelism 是物理运算符。

注意 注意

如果查询被编译成并行查询,但在运行时作为串行查询运行,则由 SET STATISTICS XML 或通过使用 SQL Server Management Studio 中的“包括实际的执行计划”选项生成的显示计划输出将不包含 Parallelism 运算符的 RunTimeInformation 元素。 在 SET STATISTICS PROFILE 输出中,为 Parallelism 运算符显示的实际行计数和实际执行数将为零。 出现任何一种情况时,都说明 Parallelism 运算符只在编译查询时使用,未在运行时查询计划中使用。 请注意,如果服务器上的并发负荷很高,则并行查询计划有时会以串行方式运行。

Parameter Table Scan 运算符图标

Parameter Table Scan

Parameter Table Scan 运算符扫描在当前查询中用作参数的表。 该运算符一般用于存储过程内的 INSERT 查询。 Parameter Table Scan既是一个逻辑运算符,也是一个物理运算符。

Partial Aggregate

Partial Aggregate 用于并行计划中。 它将聚合功能应用到尽可能多的输入行中,以便不必执行向磁盘写入数据的操作(称为“溢出”)。Hash Match 是实现分区聚合的唯一一个物理运算符(迭代器)。 Partial Aggregate 是一个逻辑运算符。

Population Query Cursor 运算符图标

Population Query

Population Query 运算符在打开游标时填充游标的工作表。

Refresh Query Cursor 运算符图标

Refresh Query

Refresh Query 运算符为提取缓冲区中的行提取当前数据。

Remote Delete 运算符图标

Remote Delete

Remote Delete 运算符用于从远程对象中删除输入行。 Remote Delete 既是一个逻辑运算符,也是一个物理运算符。

Remote Index Seek Showplan 运算符

Remote Index Scan

Remote Index Scan 运算符可以扫描在 Argument 列中指定的远程索引。 Remote Index Scan 既是一个逻辑运算符,也是一个物理运算符。

Remote Index Seek Showplan 运算符

Remote Index Seek

Remote Index Seek 运算符利用远程索引对象的查找功能来检索行。 Argument 列包含所使用的远程索引名称和 SEEK:() 谓词。 Remote Index Seek 是一个逻辑物理运算符。

Remote Insert 运算符图标

Remote Insert

Remote Insert 运算符将输入行插入到远程对象。 Remote Insert 既是一个逻辑运算符,也是一个物理运算符。

Remote Query 运算符图标

Remote Query

Remote Query 运算符将查询提交给远程源。 发送给远程服务器的查询文本显示在 Argument 列中。 Remote Query 既是一个逻辑运算符,也是一个物理运算符。

Remote Scan 运算符图标

Remote Scan

Remote Scan 运算符扫描远程对象。 远程对象的名称显示在 Argument 列中。 Remote Scan 既是一个逻辑运算符,也是一个物理运算符。

Remote Update 运算符图标

Remote Update

Remote Update 运算符将更新远程对象中的输入行。 Remote Update 既是一个逻辑运算符,也是一个物理运算符。

Repartition Streams Parallelism 运算符图标

Repartition Streams

Repartition Streams 运算符使用多个流并生成多个记录流。 记录的内容和格式不会改变。 如果查询优化器使用位图筛选器,则输出流中行的数量将减少。 输入流中的每个记录都放入一个输出流中。 如果该运算符保留次序,则必须对所有输入流排序并将它们合并到几个有序的输出流中。 如果将输出分区,那么 Argument 列会包含 PARTITION COLUMNS:() 谓词和分区列。如果输出已经排序,则 Argument 列包含一个 ORDER BY:() 谓词和已经排序的列。 Repartition Streams 是一个逻辑运算符。 该运算符只用于并行查询计划中。

Result 语言元素图标

Result

Result 运算符是查询计划结束时返回的数据。 它通常是显示计划的根元素。 Result 是一个语言元素。

RID Lookup 运算符图标

RID Lookup

RID Lookup 是使用提供的行标识符 (RID) 在堆上进行的书签查找。 Argument 列包含用于查找表中的行的书签标签和从中查找行的表的名称。 RID Lookup 通常带有 NESTED LOOP JOIN。 RID Lookup 是一个物理运算符。 有关书签查找的详细信息,请参阅 MSDN SQL Server 博客中的 Bookmark Lookup(书签查找)。

Right Anti Semi Join

Right Anti Semi Join 运算符输出第二个(底端)输入中与第一个(顶端)输入中的任何行都不匹配的每一行。 匹配行的定义是满足Argument 列内的谓词的行(如果不存在谓词,则每行都是一个匹配行)。 Right Anti Semi Join 是一个逻辑运算符。

Right Outer Join

Right Outer Join 运算符返回满足第二个(底端)输入与第一个(顶端)输入的每个匹配行的联接的每行。 此外,它还返回第二个输入中在第一个输入中没有匹配行的任何行,即与 NULL 联接。 如果 Argument 列内不存在任何联接谓词,则每行都是一个匹配行。 Right Outer Join 是一个逻辑运算符。

Right Semi Join

第一个(顶端)输入有匹配行时,Right Semi Join 运算符返回第二个(底端)输入中的每一行。 如果 Argument 列内不存在任何联接谓词,则每行都是一个匹配行。 Right Semi Join 是一个逻辑运算符。

Row Count Spool 运算符图标

Row Count Spool

Row Count Spool 运算符扫描输入,计算现有的行数并返回相同数目的不包含任何数据的行。 必须检查现有行数(而非行中包含的数据)时,使用此运算符。 例如,如果 Nested Loops 运算符执行左半联接操作且联接谓词应用于内部输入,则可以在 Nested Loops 运算符内部输入的顶部放置行计数假脱机。 这样,Nested Loops 运算符就可以确定行计数假脱机输出的行数(因为不需要内侧的实际数据)以决定是否返回外部行。 Row Count Spool 是一个物理运算符。

Segment 运算符图标

Segment

Segment 既是一个物理运算符,也是一个逻辑运算符。 它基于一个或多个列的值将输入集划分成多个段。 这些列显示为 Segment 运算符中的参数。 然后此运算符每次输出一个段。

Segment Repartition

在并行查询计划中,有时存在迭代器的概念性区域。 此类区域中的所有迭代器都可通过并行线程执行。 这些区域本身必须串行执行。 单个区域内的某些 Parallelism 迭代器称为 Branch Repartition 两个这样的区域边界上的 Parallelism 迭代器称为 Segment RepartitionBranch Repartition  Segment Repartition 是逻辑运算符。

Sequence 运算符图标

Sequence

Sequence 运算符驱动大范围的更新计划。 就其功能而言,该运算符按顺序(从上到下)执行每个输入。 每个输入通常是不同对象的更新。 该运算符只返回其上一个(底端)输入中的行。 Sequence 既是一个逻辑运算符,也是一个物理运算符。

Sequence Project 运算符图标

Sequence Project

Sequence Project 运算符将添加列以便计算有序集。 它基于一个或多个列的值将输入集划分成多个段。 然后此运算符每次输出一个段。这些列在 Sequence Project 运算符中作为参数显示。 Sequence Project 既是一个逻辑运算符,也是一个物理运算符。

Snapshot Cursor 运算符图标

Snapshot

Snapshot 运算符创建一个看不到其他人所做更改的游标。

Sort 运算符图标

Sort

Sort 运算符可对所有传入的行进行排序。 Argument 列包含 DISTINCT ORDER BY:() 谓词(如果此操作删除了重复项),或 ORDER BY:() 谓词(如果对逗号分隔的列列表进行排序)。 如果按升序对列排序,则使用值 ASC 作为列的前缀;如果按降序对列排序,则使用值 DESC 作为列的前缀。 Sort 既是一个逻辑运算符,也是一个物理运算符。

Split 运算符图标

Split

Split 运算符用于优化更新处理。 它将每个更新操作拆分成删除和插入操作。 Split 既是一个逻辑运算符,也是一个物理运算符。

Spool 运算符图标

Spool

Spool 运算符将中间查询结果保存到 tempdb 数据库中。

Stream Aggregate 运算符图标

Stream Aggregate

Stream Aggregate 运算符按一列或多列对行分组,然后计算由查询返回的一个或多个聚合表达式。 此运算符的输出可供查询中的后续运算符引用和/或返回到客户端。 Stream Aggregate 运算符要求输入在组中按列进行排序。 如果由于前面的 Sort 运算符或已排序的索引查找或扫描导致数据尚未排序,则优化器将在此运算符前面使用一个 Sort 运算符。 在 SHOWPLAN_ALL 语句或 SQL Server Management Studio 的图形执行计划中,GROUP BY 谓词中的列会列在 Argument 列中,而聚合表达式列在 Defined Values 列中。 Stream Aggregate 是一个物理运算符。

Switch 运算符图标

Switch

Switch 是一种特殊类型的串联迭代器,它具有 n 个输入。 有一个表达式与每个 Switch 运算符关联。 根据表达式的返回值(在 0 到 n-1 之间),Switch 将适当的输入流复制到输出流。 Switch 的一种用途是与某些运算符(如 TOP 运算符)一起实现涉及快进游标的查询计划。Switch 既是一个逻辑运算符,也是一个物理运算符。

Table Delete 运算符图标

Table Delete

Table Delete 物理运算符删除查询执行计划的 Argument 列中所指定表中的行。

Table Insert 运算符图标

Table Insert

Table Insert 运算符将输入的行插入到在查询执行计划的 Argument 列指定的表中。 Argument 列还包含一个 SET:() 谓词,用于指示为每一列设置的值。 如果 Table Insert 的插入值没有子项,插入的行则来自 Insert 运算符本身。 Table Insert 是一个物理运算符。

Table Merge 运算符

Table Merge

Table Merge 运算符可将合并数据流应用到堆。 该运算符可在其 Argument 列中所指定的表中删除、更新或插入行。 执行的实际操作取决于该运算符的 Argument 列中指定的 ACTION 列的运行时值。 Table Merge 是一个物理运算符。

Table Scan 运算符图标

Table Scan

Table Scan 运算符从查询执行计划的 Argument 列所指定的表中检索所有行。 如果 WHERE:() 谓词出现在 Argument 列中,则仅返回满足此谓词的那些行。 Table Scan 既是一个逻辑运算符,也是一个物理运算符。

Table Spool 运算符图标

Table Spool

Table Spool 运算符扫描输入,并将各行的一个副本放入隐藏的假脱机表中,此表存储在 tempdb 数据库中并且仅在查询的生存期内存在。如果重绕该运算符(例如通过 Nested Loops 运算符重绕),但不需要任何重新绑定,则将使用假脱机数据,而不用重新扫描输入。 Table Spool 是一个物理运算符。

Table Update 运算符图标

Table Update

Table Update 物理运算符更新查询执行计划的 Argument 列中所指定表中的输入行。 SET:() 谓词确定每个更新列的值。 可以在 SET 子句中、此运算符内的其他位置以及此查询内的其他位置引用这些值。

Table-valued Function 运算符图标

Table-valued Function

Table-valued Function 运算符计算表值函数(Transact-SQL 或 CLR)并将结果行存储在 tempdb 数据库中。 当父迭代器请求这些行时,Table-valued Function 将返回 tempdb 中的行。

调用表值函数的查询生成具有 Table-valued Function 迭代器的查询计划。 可以使用不同的参数值计算 Table-valued Function

  • Table-valued Function XML Reader 输入 XML BLOB 作为参数,并生成一个按 XML 文档顺序表示 XML 节点的行集。 其他输入参数可能会将返回的 XML 节点限于 XML 文档的子集。

  • Table Valued Function XML Reader with XPath filter 是一种特殊的 XML Reader Table-valued Function,它将输出限于满足 XPath 表达式的 XML 节点。

Table-valued Function 既是一个逻辑运算符,也是一个物理运算符。

Top 运算符图标

Top

Top 运算符扫描输入,但仅基于排序顺序返回最前面的指定行数或行百分比。 Argument 列可以包含要检查重复值的列的列表。 在更新计划中,Top 运算符用于强制实施行计数限制。 Top 既是一个逻辑运算符,也是一个物理运算符。 Top 既是一个逻辑运算符,也是一个物理运算符。

Top N Sort

Top N Sort  Sort 迭代器类似,差别仅在于前者需要前 N 行,而不是整个结果集。 如果 N 的值较小,SQL Server 查询执行引擎将尝试在内存中执行整个排序操作。 如果 N 的值较大,查询执行引擎将使用更通用的排序方法(该方法不采用 N 作为参数)重新排序。

Extended 运算符 (UDX) 图标

UDX

扩展运算符 (UDX) 可以实现 SQL Server 中的一种 XQuery 或 XPath 操作。 所有 UDX 运算符既是逻辑运算符,又是物理运算符。

扩展运算符 (UDX) FOR XML 用于将其输入的关系行集序列化为 XML 表示形式,并以单个输出行、单个 BLOB 列的形式存储。 它是区分顺序的 XML 聚合运算符。

扩展运算符 (UDX) XML SERIALIZER 是区分顺序的一种 XML 聚合运算符。 它以 XML 文档顺序输入表示 XML 节点或 XQuery 标量的行,并在单个输出行、单个 XML 列中生成序列化的 XML BLOB。

扩展运算符 (UDX) XML FRAGMENT SERIALIZER 是一种特殊类型的 XML SERIALIZER,用于处理表示在 XQuery 插入数据修改扩展中插入的 XML 片断的输入行。

扩展运算符 (UDX) XQUERY STRING 计算表示 XML 节点的输入行的 XQuery 字符串值。 它是一个区分顺序的字符串聚合运算符。 它输出一行多列,表示包含输入字符串值的 XQuery 标量。

扩展运算符 (UDX) XQUERY LIST DECOMPOSER 是一个 XQuery 列表分解运算符。 对于表示 XML 节点的每个输入行,它至少生成表示 XQuery 标量的一个行,如果输入的是 XSD 列表类型的行,则每个行都包含一个列表元素值。

扩展运算符 (UDX) XQUERY DATA 在表示 XML 节点的输入行上计算 XQuery fn:data() 函数的值。 它是一个区分顺序的字符串聚合运算符。 它输出一行多列,表示包含 fn:data() 结果的 XQuery 标量。

扩展运算符 XQUERY CONTAINS 在表示 XML 节点的输入行上计算 XQuery fn:contains() 函数的值。 它是一个区分顺序的字符串聚合运算符。 它输出一行多列,表示包含 fn:contains() 结果的 XQuery 标量。

扩展运算符 UPDATE XML NODE 更新 XML 类型的 modify() 方法中 XQuery 替换数据修改扩展的 XML 节点。

Union

Union 运算符扫描多个输入,输出扫描的每一行并删除重复项。 Union 是一个逻辑运算符。

Update(数据库引擎)运算符图标

Update

Update 运算符更新在查询执行计划的 Argument 列中所指定对象中的每一输入行。 Update 是一个逻辑运算符。 物理运算符为 Table UpdateIndex Update  Clustered Index Update

While 语言元素图标

While

While 运算符实现 Transact-SQL while 循环。 While 是一个语言元素。

Table Spool 运算符图标

Window Spool

Window Spool 运算符将每个行扩展为表示与行关联的窗口的行集。 在查询中,OVER 子句定义查询结果集内的窗口和窗口函数,然后计算窗口中的每个行的值。 Window Spool 既是一个逻辑运算符,也是一个物理运算符。

 

https://msdn.microsoft.com/zh-cn/library/ms187840%28v=sql.105%29.aspx?f=255&MSPPError=-2147217396

Merge Interval 运算符可合并多个(可能重叠的)间隔以得出最小的不重叠间隔,然后将其用于查找索引项。此运算符通常出现在 Constant Scan 运算符中的一个或多个 Compute Scalar 运算符上方,Constant Scan 运算符构造了此运算符所合并的间隔(表示为一行中的多个列)。

Merge Interval 既是一个逻辑运算符,也是一个物理运算符。

Merge Interval 运算符图标 图形执行计划图标

 

 

Sequence Project 运算符图标

Sequence Project

Sequence Project 运算符将添加列以便计算有序集。 它基于一个或多个列的值将输入集划分成多个段。 然后此运算符每次输出一个段。这些列在 Sequence Project 运算符中作为参数显示。 Sequence Project 既是一个逻辑运算符,也是一个物理运算符。

 关系运算

投影 projection,投影运算也是单目运算,关系R上的投影是从R中选择出若干属性列,组成新的关系,即对关系在垂直方向进行的运算,从左到右按照指定的若干属性以及顺序取出相应列,删除重复元组

投影运算是从列的角度进行的运算,这正是选取运算和投影运算的区别所在,选取运算是从关系的水平方向向上进行运算,而投影运算则是从关系的垂直方向上进行的

 数据库原理及应用教程 第3版  陈志泊主编

 

本文版权归作者所有,未经作者同意不得转载。

posted @ 2013-10-06 12:54  桦仔  阅读(12093)  评论(24编辑  收藏  举报