SQLSERVER中的假脫機spool

SQLSERVER中的假脫機spool

我發現網上對於假脫機的解釋都很是零散,究竟假脫機是什麼?html

這幾天在家裏研究了一下,收集了不少網上的資料node

假脫機是中文的翻譯,而英文的名字叫作 spoolweb

 

在徐老師寫的《SQLSERVER企業級平臺管理實踐》裏提到了一下假脫機算法

在SQLSERVER I/O問題的那一節sql

在性能監視器裏,有一個計數器「worktables/sec」 :數據庫

每秒建立的工做表數。例如,工做表可用於存儲查詢假脫機(query spool),LOB變量,XML變量,表變量,遊標的臨時結果express

在《剖析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)裏

防止因爲更新非彙集索引的非彙集索引鍵而引發的非彙集索引從新排序,形成數據更新錯誤的問題


總結

 

實際上這些假脫機運算符的本質跟臨時表和表變量是同樣的,都是以空間換時間,以達到性能上的平衡!

文章最後面附上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版  陳志泊主編

 

本文版權歸做者全部,未經做者贊成不得轉載。

相關文章
相關標籤/搜索