目錄
最近使用到sqlserver数据库并要对查询语句进行调优,之前接触的不多,搜索网上一些帖子和查阅微软msdn资料对sqlserver的查询计划才大致了解了一些,用这篇文章做个总结。本文主要侧重对查询计划的理解,尤其是对微软复杂的运算函数的理解,如理解有误请指出。
如何查看查询计划
关于查询计划的说明
 
具体查询计划分析
初步分析计划
关于表扫描
首頁 資料庫 mysql教程 分析sqlserver查询计划

分析sqlserver查询计划

Jun 07, 2016 pm 03:35 PM
sqlserver 分析 查詢 計劃

最近使用到sqlserver数据库并要对查询语句进行调优,之前接触的不多,搜索网上一些帖子和查阅微软msdn资料对sqlserver的查询计划才大致了解了一些,用这篇文章做个总结。本文主要侧重对查询计划的理解,尤其是对微软复杂的运算函数的理解,如理解有误请指出

最近使用到sqlserver数据库并要对查询语句进行调优,之前接触的不多,搜索网上一些帖子和查阅微软msdn资料对sqlserver的查询计划才大致了解了一些,用这篇文章做个总结。本文主要侧重对查询计划的理解,尤其是对微软复杂的运算函数的理解,如理解有误请指出。

如何查看查询计划

先介绍一下如何查看查询计划。使用【SQL Server Management Studio】进行查询,大致有如下几个方式看查询计划

1、set showplan_all on 在查询前显示计划显示形式为每个查询步骤一行

2、set statistics profile on 在查询后显示计划并包括每个查询步骤的扫描行数和执行次数

3、选中sql语句按Ctrl + L(同工具栏显示预估的查询计划按钮) 以图形方式显示查询计划这个还可以使用setshowplan_xml on命令后者生成的xmlSQL Server Management Studio打开就是图形查询计划

 

关于查询计划的说明

在msdn上找到set showplan_all on返回结果各列的说明

 

分析sqlserver查询计划

 

 

具体查询计划分析

初步分析计划

开启set showplan_all on

执行如下sql

SELECT dbll.N_SZJY GBM, sum(data.N_SL) NNum

FROM DB_SHARE.dbo.DA_JGXT_VW_QBF_FFJL data

left join DB_SHARE.dbo.T_DBLL dbll

ON dbll.C_ZFBH = data.ZFBH and dbll.D_KSRQ  data.D_RQ and (dbll.D_JSRQ >= data.D_RQ ORdbll.D_JSRQ IS NULL)

where data.D_RQ >= '2012-07-02 00:00:00.0' and data.D_RQ  '2012-08-01 23:59:59.0'

GROUP BY dbll.N_SZJY

 

执行计划左半部分

 

分析sqlserver查询计划

 

 接上图计划的右半部分如下:

 

分析sqlserver查询计划

 

 

Sqlserver的执行计划一出来给人一种特别复杂的感觉有木有,其中命令特别多,还可以看到我们没有写出来的语句比如Expr1006Expr1012Bmk1003这都是什么啊?

一步一步来先看看执行计划中每个列的说明

msdn有对执行计划所有运算符的说明

http://msdn.microsoft.com/zh-cn/library/ms191158(v=sql.105)

根据msdn说明,对上面的执行计划分析如下,对于整个计划树的执行是从下到上,从叶子到根的,最上边是整个查询语句:

12、【RID Lookup(OBJECT:([DB_SHARE].[dbo].[T_DBLL] AS [dbll]), SEEK:([Bmk1003]=[Bmk1003]), WHERE:([DB_SHARE].[dbo].[T_DBLL].[D_JSRQ] as [dbll].[D_JSRQ]>=[DB_SHARE].[dbo].[DA_JGXT_VW_QBF_FFJL].[D_RQ] as [data].[D_RQ] OR [DB_SHARE].[dbo].[T_DBLL].[D_JSRQ] as [dbll].[D_JSRQ] IS NULL) LOOKUP ORDERED FORWARD)

这是一个书签查找步骤,,RIDrecord id Lookup是使用行标示符在堆上进行书签查找,SEEK后面有一个[Bmk1003]=[Bmk1003],看命名是一个书签,在11行的DefinedValues列有对此的定义。

 

11、【Index Seek(OBJECT:([DB_SHARE].[dbo].[T_DBLL].[I_DBLL_ZFBH_KSRQ] AS [dbll]), SEEK:([dbll].[C_ZFBH]=[DB_SHARE].[dbo].[DA_JGXT_VW_QBF_FFJL].[ZFBH] as [data].[ZFBH] AND [dbll].[D_KSRQ]

这是使用I_DBLL_ZFBH_KSRQ索引在T_DBLL表检索数据,SEEK部分是检索的具体条件,此步骤定义了Bmk100311步应该先于12执行,看来在同一层级下顺序是从上到下的。

11步骤和12步骤的意思是先用I_DBLL_ZFBH_KSRQ索引检索数据,再使用书签查找的方式获取每一行的其他数据。因为I_DBLL_ZFBH_KSRQ是一个非聚集索引,它只包含索引列的数据,实际上对T_DBLL检索出的数据列还包括N_SZJYD_JSRQ,前者要进行Group,后者是进行与DA_JGXT_VW_QBF_FFJLD_RQ的比对,查询语句涉及到非聚集索引不包含的列时就要通过书签查找或聚集索引查找来提取非索引列(T_DBLL此时没有聚集索引,只能用书签查找的方式)。

 

10、【Nested Loops(Inner Join, OUTER REFERENCES:([Bmk1003]))

一次嵌套循环连接,将1112步骤数据连接起来,11步骤的数据作为Outer,在进行书签查找的父步骤一般都是Nested Loops Join,这基本符合内表较大且有索引的条件。

 

9、【Table Scan(OBJECT:([DB_SHARE].[dbo].[DA_JGXT_VW_QBF_FFJL] AS [data]), WHERE:([DB_SHARE].[dbo].[DA_JGXT_VW_QBF_FFJL].[D_RQ] as [data].[D_RQ]>='2012-07-02 00:00:00.000' AND [DB_SHARE].[dbo].[DA_JGXT_VW_QBF_FFJL].[D_RQ] as [data].[D_RQ]

DA_JGXT_VW_QBF_FFJL表全表扫描数据,条件是D_RQ在一个时间段内。显然这是需要增加索引的

 

8、【Parallelism(Repartition Streams, RoundRobin Partitioning)

这是什么呢?看看微软古板的说明“Parallelism 运算符执行分发流、收集流和对流重新分区逻辑操作。Argument 列可以包含一个 PARTITION COLUMNS:() 谓词和一个以逗号分隔的分区列的列表。Argument 列还可以包含一个 ORDER BY:() 谓词,以列出分区过程中要保留排序顺序的列。Repartition Streams 运算符处理多个流并生成多个记录流。记录的内容和格式不会改变”。大约是这样,Parallelism表示查询会被并行执行(如果服务器负荷太高可能最终不会并行),这算是sqlserver的一个优化处理,如果服务器处理多任务能力强这就会比串行更有效率,这个并行应该是表示和其他任务的关系,由于10步骤会依赖8步骤的数据,8步骤应该先于10步骤执行

 

7、【Nested Loops(Left Outer Join, OUTER REFERENCES:([data].[ZFBH], [data].[D_RQ], [Expr1012]) WITH UNORDERED PREFETCH)

一次嵌套循环连接,它将810两个步骤得到的数据连接,Outer表是DA_JGXT_VW_QBF_FFJL,因OUTER REFERENCES中的字段都是属于该表。

这里出现了Expr1012,这是个神秘的列,联系上下语句,这个列应该是T_DBLLN_SZJY,因为后面要用此列数据进行分组。

 

6、【Sort(ORDER BY:([dbll].[N_SZJY] ASC))

使用N_SZJY列排序,后面要进行Group,这里必须先进行一次排序

 

5、【Stream Aggregate(GROUP BY:([dbll].[N_SZJY]) DEFINE:([partialagg1007]=COUNT_BIG([DB_SHARE].[dbo].[DA_JGXT_VW_QBF_FFJL].[N_SL] as [data].[N_SL]), [partialagg1009]=SUM([DB_SHARE].[dbo].[DA_JGXT_VW_QBF_FFJL].[N_SL] as [data].[N_SL])))

名词解释,“Stream Aggregate 运算符按一列或多列对行分组,然后计算查询返回的一个或多个聚合表达式。此运算符的输出可供查询中的后续运算符引用和/或返回到客户端。Stream Aggregate 运算符要求输入在组中按列进行排序。如果由于前面的 Sort 运算符或已排序的索引查找或扫描导致数据尚未排序,优化器将在此运算符前面使用一个 Sort 运算符

继续根据微软生硬的解释进行分析,这行的处理就是进行一个分组,因为GROUP BY dbll.N_SZJY一句而产生此行处理。其中还有一个COUNT_BIG,这是一个类似COUNT的函数,区别是前者返回bigint后者返回int,不过语句中只有个sum没有count,为什么还要计算呢?跳过先。

 

4、【Parallelism(Gather Streams, ORDER BY:([dbll].[N_SZJY] ASC))

又一个并行查询。“Gather Streams 运算符仅用在并行查询计划中。Gather Streams 运算符处理几个输入流并通过组合这几个输入流生成单个记录输出流。不更改记录的内容和格式。如果此运算符保留顺序,则所有的输入流都必须有序。如果输出已排序,则参数列包含一个 ORDER BY:() 谓词和正在排序的列名称。 Gather Streams函数把输入流组合,不过4行只有一个子节点,我理解到4之前仍存在两个输入流,分别来自DA_JGXT_VW_QBF_FFJL表和T_DBLL表,上面的步骤对两个输入流分别有索引条件过滤和分组,但仍然没有整合,在Gather Streams步骤将两个输入流数据整合起来,那么之后的数据看到的就是一个输入流了

3、【Stream Aggregate(GROUP BY:([dbll].[N_SZJY]) DEFINE:([globalagg1008]=SUM([partialagg1007]), [globalagg1010]=SUM([partialagg1009])))

再次进行流聚合。这里和第5步骤的不同在哪里

5步骤DefinedValues中有如下定义

[partialagg1007]=COUNT_BIG([DB_SHARE].[dbo].[DA_JGXT_VW_QBF_FFJL].[N_SL] as [data].[N_SL]), [partialagg1009]=SUM([DB_SHARE].[dbo].[DA_JGXT_VW_QBF_FFJL].[N_SL] as [data].[N_SL])

而此步骤执行了

[globalagg1008]=SUM([partialagg1007]), [globalagg1010]=SUM([partialagg1009])

这就是它们的不同,此步骤再次用N_SZJY分组对之前的计算结果求和,这应该是个避免整合流后分组数据出现重复的操作。

2、【Compute Scalar(DEFINE:([Expr1006]=CASE WHEN [globalagg1008]=(0) THEN NULL ELSE [globalagg1010] END))

Compute Scalar的意思是计算标量。标量,相对于向量而言,无方向数据,就是计算一个数值。这个步骤sqlserver优化器执行了一个case whenglobalagg1008是针对每个N_SZJYcountN_SL),看来之前自动执行count是为了此步,globalagg1010是针对每个N_SZJYsumN_SL),对照我们的语句是SELECT dbll.N_SZJY GBM, sum(data.N_SL) NNum此步骤的意图看来sqlserver会对sum特殊情况的检测,对于整个语句而言就是如果T_DBLL表中N_SZJY列有数据而DA_JGXT_VW_QBF_FFJLN_SL都为null,那么sum就返回null

 

1、【整条语句】。作为查询计划树的根节点,在计划列表中大多数列都是Null,这个计划的估算行是12行,仅供参考,实际查询的结果是1

 

关于表扫描

Sql Server 会有以下方法来查找您需要的数据记录:
1. 
Table Scan】:遍历整个表,查找所匹配的记录行。这个操作将会一行一行的检查,当然,效率也是最差的。
2. 
Index Scan】:根据非聚集索引,扫描索引的全部记录,查找所匹配的记录行,匹配的条件可从查询计划的Argument 列中看到。比第一种方式的查找范围要小(B+索引叶子之间有指针类似链表),因此比【Table Scan】要快。
3. 
Index Seek】:根据非聚集索引,定位(获取)记录的存放位置,然后取得记录(这会使用B+索引查找树的定位算法,基本一条记录2-4IO,取决于表数据量产生的索引树高度),这个方式比起前二种方式会更快。
4. 
Clustered Index Scan】:根据聚集索引扫描全部记录。这个的效率要根据实际情况分析。出现这个步骤可能是效率很差的表现,因为如果条件中的列没有索引,数据库引擎在提取数据的时会考虑进行优化,基于磁盘顺序读比随机读快的原理,数据按照聚集索引的顺序存放,那么用聚集索引来提取数据是一种对更差方式的优化。

比如DA_JGXT_VW_QBF_FFJL表有715455条记录,

如下记录返回8条记录,优化器使用Clustered Index Scan

select * from DB_SHARE.dbo.DA_JGXT_VW_QBF_FFJL where N_ID = 14000

执行时间10s,计划如下,IO消耗是7.21多,此时使用聚集索引扫描来顺序提取数据,这个步骤在这里就是避免更差的随机磁盘读取

 

分析sqlserver查询计划

  

如下语句返回24条记录优化器使用Clustered Index Seek

select * from DB_SHARE.dbo.DA_JGXT_VW_QBF_FFJL where D_RQ = '2012-07-25 00:00:00.0'

执行时间0s毫秒级,计划如下,IO消耗是0.000232

 

分析sqlserver查询计划

  

 而直接使用select top 10 * from DB_SHARE.dbo.DA_JGXT_VW_QBF_FFJL查询计划也会使用Clustered Index Scan

select top 10 * from DB_SHARE.dbo.DA_JGXT_VW_QBF_FFJL

|--Top(TOP EXPRESSION:((10)))

|--Clustered Index Scan (OBJECT:([DB_SHARE].[dbo].[DA_JGXT_VW_QBF_FFJL].[I_DA_JGXT_VW_QBF_FFJL_RQ]))

对于这个无条件的语句这个计划已经是最优的了
5. 
Clustered Index Seek】:根据聚集索引获取记录,不解释,最快!

 

优化Table Scan

使用日期过滤数据,对D_RQ建立聚集索引 

CREATE CLUSTERED INDEX I_DA_JGXT_VW_QBF_FFJL_RQ ON dbo.DA_JGXT_VW_QBF_FFJL(D_RQ)

 

再次查看执行计划

左半部分:

 

 分析sqlserver查询计划

 

右半部分

 

分析sqlserver查询计划

  

Table Scan变为Clustered Index Seek,看右半部分计划中的EstimateRowsEstimateIOEstimateCPUTocalSubTreeCost等都有很大提升

优化RID Lookup

前面提到RID Lookup是使用非聚集索引时提取了索引外的列产生的一种操作,中文解释为书签查找。

微软有一篇专门的文章http://blogs.msdn.com/b/craigfr/archive/2006/06/30/652639.aspx对此作出了解释。每次书签查找会产生一次随机IO,随机IO对于磁盘来说是比较耗费资源的,虽然sqlserver优化器认为这个比不用索引的消耗小些因而选择了这个方式,但可能的情况下我们还是要考虑优化。

优化书签查找的方式大致两种,一种是给目前已经使用的索引加入要查询的列,使得查询的列都在索引中;另一种是使索引成为聚集索引。那么可以考虑创建(C_ZFBH, D_KSRQ, D_JSRQ, N_SZJY)4键联合索引或将(C_ZFBH,D_KSRQ)的索引改为聚集索引。

我先采用了聚集索引优化,查询计划的IOCPUCost都有所提升,主要是TotalSubTreeCost一项提升较多。按照微软对此项的说明为查询开销,这是一个综合的数值,一般这个开销较小的更好,不过也不绝对。

  

分析sqlserver查询计划

 

那么如果用4键联合索引呢?

我发现对比两者的查询计划相差很小,于是我用了set statistics profile on来查看实际的执行情况,这个开关比前面的计划多两列,会返回每个步骤的实际扫描行数和执行次数

列名

说明

Rows

各运算符生成的实际行数

Executes

运算符执行的次数

先看聚集索引,本次执行耗费8s

  

分析sqlserver查询计划

 

 

再看4键联合索引,本次执行耗费12s,值得一提的是此时两个索引都存在,是sqlserver优化器选择了4键联合索引

 

分析sqlserver查询计划

 

 

相比之下,后者的TotalSubTreeCost较小因IO少,但是前者实际扫描的行数较少,且执行时间更短。我在执行前已使用了dbcc dropcleanbuffersdbcc freeproccache清除缓存,不过我使用的数据库是虚拟机,在执行效率上经常有波动,后者的执行时间长可能因为索引还没有全部加载到内存中,实际测试时有时后者的时间更短。不过鉴于D_JSRQ是存在空值并且检索的时候都要使用(D_JSRQ>日期 or D_JSRQ is null)这样的条件,前者可能更好。

 

看懂Sqlserver查询计划

http://www.cnblogs.com/fish-li/archive/2011/06/06/2073626.html

msdn逻辑运算符和物理运算符引用

http://msdn.microsoft.com/zh-cn/library/ms191158(v=sql.105)

本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

AI Hentai Generator

AI Hentai Generator

免費產生 AI 無盡。

熱門文章

R.E.P.O.能量晶體解釋及其做什麼(黃色晶體)
3 週前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳圖形設置
3 週前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.如果您聽不到任何人,如何修復音頻
4 週前 By 尊渡假赌尊渡假赌尊渡假赌
WWE 2K25:如何解鎖Myrise中的所有內容
1 個月前 By 尊渡假赌尊渡假赌尊渡假赌

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

sqlserver怎麼匯入mdf文件 sqlserver怎麼匯入mdf文件 Apr 08, 2024 am 11:41 AM

匯入步驟如下:將 MDF 檔案複製到 SQL Server 的資料目錄(通常為 C:\Program Files\Microsoft SQL Server\MSSQL\DATA)。在 SQL Server Management Studio(SSMS)中,開啟資料庫並選擇「附加」。點選“新增”按鈕,選擇 MDF 檔案。確認資料庫名稱,點選確定按鈕即可。

sqlserver資料庫中已存在名為的物件怎麼解決 sqlserver資料庫中已存在名為的物件怎麼解決 Apr 05, 2024 pm 09:42 PM

對於 SQL Server 資料庫中已存在同名對象,需要採取下列步驟:確認物件類型(表格、檢視、預存程序)。如果物件為空,可使用 IF NOT EXISTS 跳過建立。如果物件有數據,使用不同名稱或修改結構。使用 DROP 刪除現有物件(謹慎操作,建議備份)。檢查架構更改,確保沒有引用刪除或重新命名的物件。

sqlserver服務無法啟動怎麼辦 sqlserver服務無法啟動怎麼辦 Apr 05, 2024 pm 10:00 PM

當 SQL Server 服務無法啟動時,可採取下列步驟解決:檢查錯誤日誌以確定根本原因。確保服務帳戶具有啟動服務的權限。檢查依賴項服務是否正在執行。禁用防毒軟體。修復 SQL Server 安裝。如果修復不起作用,重新安裝 SQL Server。

怎麼查看sqlserver連接埠號 怎麼查看sqlserver連接埠號 Apr 05, 2024 pm 09:57 PM

若要查看 SQL Server 連接埠號碼:開啟 SSMS,連線到伺服器。在物件資源管理器中找到伺服器名稱,右鍵單擊它,然後選擇“屬性”。在「連線」標籤中,查看「TCP 連接埠」欄位。

sqlserver資料庫在哪裡 sqlserver資料庫在哪裡 Apr 05, 2024 pm 08:21 PM

SQL Server 資料庫檔案通常儲存在下列預設位置:Windows: C:\Program Files\Microsoft SQL Server\MSSQL\DATALinux: /var/opt/mssql/data可透過修改資料庫檔案路徑設定來自訂資料庫檔案位置。

sqlserver誤刪資料庫怎麼恢復 sqlserver誤刪資料庫怎麼恢復 Apr 05, 2024 pm 10:39 PM

若誤刪 SQL Server 資料庫,可採取下列步驟還原:停止資料庫活動;備份日誌檔案;檢查資料庫日誌;復原選項:從備份還原;從交易日誌還原;使用 DBCC CHECKDB;使用第三方工具。請定期備份資料庫並啟用交易日誌以防止資料遺失。

sqlserver安裝失敗怎麼樣刪除乾淨 sqlserver安裝失敗怎麼樣刪除乾淨 Apr 05, 2024 pm 11:27 PM

如果 SQL Server 安裝失敗,可透過下列步驟清理:解除安裝 SQL Server刪除註冊表項刪除檔案和資料夾重新啟動計算機

12306怎麼查詢歷史購票紀錄 查看歷史購票紀錄的方法 12306怎麼查詢歷史購票紀錄 查看歷史購票紀錄的方法 Mar 28, 2024 pm 03:11 PM

12306訂票app下載最新版是一款大家非常滿意的出行購票軟體,想去哪裡就去那裡非常方便,軟體內提供的票源非常多,只需要通過實名認證就能在線購票,所有用戶的出行車票機票都可以輕鬆買到,享受不同的優惠折扣。還能提前開啟預約搶票,預約飯店、專車接送都是可以的,有了它想去哪裡就去那裡一鍵購票,出行更加簡單方便,讓大家的出行體驗更舒服,現在小編在線詳細為12306用戶帶來查看歷史購票記錄的方法。  1.打開鐵路12306,點擊右下角我的,點擊我的訂單  2.在訂單頁面點擊已支付。  3.在已支付頁

See all articles