목차
最近使用到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 기반 앱

AI Clothes Remover

AI Clothes Remover

사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool

Undress AI Tool

무료로 이미지를 벗다

Clothoff.io

Clothoff.io

AI 옷 제거제

AI Hentai Generator

AI Hentai Generator

AI Hentai를 무료로 생성하십시오.

인기 기사

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 통합 개발 환경

드림위버 CS6

드림위버 CS6

시각적 웹 개발 도구

SublimeText3 Mac 버전

SublimeText3 Mac 버전

신 수준의 코드 편집 소프트웨어(SublimeText3)

mdf 파일을 sqlserver로 가져오는 방법 mdf 파일을 sqlserver로 가져오는 방법 Apr 08, 2024 am 11:41 AM

가져오기 단계는 다음과 같습니다. MDF 파일을 SQL Server의 데이터 디렉터리(일반적으로 C:\Program Files\Microsoft SQL Server\MSSQL\DATA)에 복사합니다. SSMS(SQL Server Management Studio)에서 데이터베이스를 열고 연결을 선택합니다. 추가 버튼을 클릭하고 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 티켓 예매 앱의 최신 버전을 다운로드하세요. 모두가 매우 만족하는 여행 티켓 구매 소프트웨어입니다. 소프트웨어에서 제공되는 다양한 티켓 소스가 있어 매우 편리합니다. - 실명인증으로 온라인 구매가 가능합니다. 모든 사용자가 쉽게 여행티켓과 항공권을 구매하고 다양한 할인 혜택을 누릴 수 있습니다. 또한 사전에 예약하고 티켓을 얻을 수도 있습니다. 호텔을 예약하거나 차량으로 픽업 및 하차할 수도 있습니다. 한 번의 클릭으로 원하는 곳으로 이동하고 티켓을 구매할 수 있어 여행이 더욱 간편해지고 편리해집니다. 모든 사람의 여행 경험이 더욱 편안해졌습니다. 이제 편집자가 온라인으로 자세히 설명합니다. 12306명의 사용자에게 과거 티켓 구매 기록을 볼 수 있는 방법을 제공합니다. 1. 철도 12306을 열고 오른쪽 하단의 My를 클릭한 후 My Order를 클릭합니다. 2. 주문 페이지에서 Paid를 클릭합니다. 3. 유료페이지에서

See all articles