搜索
首页数据库mysql教程分析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
MySQL索引基数如何影响查询性能?MySQL索引基数如何影响查询性能?Apr 14, 2025 am 12:18 AM

MySQL索引基数对查询性能有显着影响:1.高基数索引能更有效地缩小数据范围,提高查询效率;2.低基数索引可能导致全表扫描,降低查询性能;3.在联合索引中,应将高基数列放在前面以优化查询。

MySQL:新用户的资源和教程MySQL:新用户的资源和教程Apr 14, 2025 am 12:16 AM

MySQL学习路径包括基础知识、核心概念、使用示例和优化技巧。1)了解表、行、列、SQL查询等基础概念。2)学习MySQL的定义、工作原理和优势。3)掌握基本CRUD操作和高级用法,如索引和存储过程。4)熟悉常见错误调试和性能优化建议,如合理使用索引和优化查询。通过这些步骤,你将全面掌握MySQL的使用和优化。

现实世界Mysql:示例和用例现实世界Mysql:示例和用例Apr 14, 2025 am 12:15 AM

MySQL在现实世界的应用包括基础数据库设计和复杂查询优化。1)基本用法:用于存储和管理用户数据,如插入、查询、更新和删除用户信息。2)高级用法:处理复杂业务逻辑,如电子商务平台的订单和库存管理。3)性能优化:通过合理使用索引、分区表和查询缓存来提升性能。

MySQL中的SQL命令:实践示例MySQL中的SQL命令:实践示例Apr 14, 2025 am 12:09 AM

MySQL中的SQL命令可以分为DDL、DML、DQL、DCL等类别,用于创建、修改、删除数据库和表,插入、更新、删除数据,以及执行复杂的查询操作。1.基本用法包括CREATETABLE创建表、INSERTINTO插入数据和SELECT查询数据。2.高级用法涉及JOIN进行表联接、子查询和GROUPBY进行数据聚合。3.常见错误如语法错误、数据类型不匹配和权限问题可以通过语法检查、数据类型转换和权限管理来调试。4.性能优化建议包括使用索引、避免全表扫描、优化JOIN操作和使用事务来保证数据一致性

InnoDB如何处理酸合规性?InnoDB如何处理酸合规性?Apr 14, 2025 am 12:03 AM

InnoDB通过undolog实现原子性,通过锁机制和MVCC实现一致性和隔离性,通过redolog实现持久性。1)原子性:使用undolog记录原始数据,确保事务可回滚。2)一致性:通过行级锁和MVCC确保数据一致。3)隔离性:支持多种隔离级别,默认使用REPEATABLEREAD。4)持久性:使用redolog记录修改,确保数据持久保存。

MySQL的位置:数据库和编程MySQL的位置:数据库和编程Apr 13, 2025 am 12:18 AM

MySQL在数据库和编程中的地位非常重要,它是一个开源的关系型数据库管理系统,广泛应用于各种应用场景。1)MySQL提供高效的数据存储、组织和检索功能,支持Web、移动和企业级系统。2)它使用客户端-服务器架构,支持多种存储引擎和索引优化。3)基本用法包括创建表和插入数据,高级用法涉及多表JOIN和复杂查询。4)常见问题如SQL语法错误和性能问题可以通过EXPLAIN命令和慢查询日志调试。5)性能优化方法包括合理使用索引、优化查询和使用缓存,最佳实践包括使用事务和PreparedStatemen

MySQL:从小型企业到大型企业MySQL:从小型企业到大型企业Apr 13, 2025 am 12:17 AM

MySQL适合小型和大型企业。1)小型企业可使用MySQL进行基本数据管理,如存储客户信息。2)大型企业可利用MySQL处理海量数据和复杂业务逻辑,优化查询性能和事务处理。

幻影是什么读取的,InnoDB如何阻止它们(下一个键锁定)?幻影是什么读取的,InnoDB如何阻止它们(下一个键锁定)?Apr 13, 2025 am 12:16 AM

InnoDB通过Next-KeyLocking机制有效防止幻读。1)Next-KeyLocking结合行锁和间隙锁,锁定记录及其间隙,防止新记录插入。2)在实际应用中,通过优化查询和调整隔离级别,可以减少锁竞争,提高并发性能。

See all articles

热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尊渡假赌尊渡假赌尊渡假赌

热工具

SecLists

SecLists

SecLists是最终安全测试人员的伙伴。它是一个包含各种类型列表的集合,这些列表在安全评估过程中经常使用,都在一个地方。SecLists通过方便地提供安全测试人员可能需要的所有列表,帮助提高安全测试的效率和生产力。列表类型包括用户名、密码、URL、模糊测试有效载荷、敏感数据模式、Web shell等等。测试人员只需将此存储库拉到新的测试机上,他就可以访问到所需的每种类型的列表。

ZendStudio 13.5.1 Mac

ZendStudio 13.5.1 Mac

功能强大的PHP集成开发环境

Atom编辑器mac版下载

Atom编辑器mac版下载

最流行的的开源编辑器

PhpStorm Mac 版本

PhpStorm Mac 版本

最新(2018.2.1 )专业的PHP集成开发工具

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)