SQL Server中TOP子句可能导致的问题以及解决办法
简介
在SQL Server中,针对复杂查询使用TOP子句可能会出现对性能的影响,这种影响可能是好的影响,也可能是坏的影响,针对不同的情况有不同的可能性。
关系数据库中SQL语句只是一个抽象的概念,不包含任何实现。很多元数据都会影响执行计划的生成,SQL语句本身并不作为生成执行计划所参考的元数据(提示除外),但TOP关键字却是直接影响执行计划的一个关键字,因此在某些情况下使用TOP会导致性能受到影响,下面我们来看集中不同的情况。
单表情况
对于单表查询(这里的所说的单表指的是不包含视图、表值函数的物理单表)来说,存在TOP基本不会对性能产生影响,如果在SQL Server中加入了TOP,那么TOP本身可以看作是一个查询提示,意味着告诉优化器“返回结果只有N行”。我们看一个简单的例子,如图1所示:

图1.指定TOP关键字的单表执行计划
由图1执行计划对比可以看出,对于有索引支撑的单表查询来说,使用TOP子句往往可以提升性能,此时TOP N的行数的N则提示查询优化器该查询返回N行,而不是使用统计信息中的数据分布,此时TOP N对于查询优化器来说是合理的。
但有些时候Grant Memory(每次执行计划生成时会预估所需的内存,如果预估内存小于执行内存,则会spill to tempdb,对性能产生非常大的影响,由于每一个版本预估内存的公式变化极大,因此不在此详细解释了)不准会产生非常高的性能影响。在开始谈这点,之前,我们先谈两个操作符:
Sort
Sort操作符是非常通用的排序操作符,在执行计划中可能会出现在多个地方,比如Merge Join之前,由于Order By导致的等。该算法非常通用,可以对非常大的结果集进行排序,该操作符是阻塞式(意味着排序结束之前数据无法流动到下一个操作符),并且需要大量内存和CPU资源。该操作符还有一个问题是当Grant Memory不足时,需要TempDB辅助完成排序,因此有极大的性能开销。
Top N Sort
TOP N Sort是适应小场景,专门针对少量查询的排序算法。对于只选择几条数据来说,对于整个结果集进行排序成本过于高昂,因此TOP N的算法是首先取第一条数据,与其他数据进行对比,看是否最大(或最小),再取第二条数据对比,依次类推,直到找到前N条数据。该算法如果行数较小,则相比SORT操作符性能提升明显,但如果N值过大,则由于下述原因该算法不合适:
1.该算法不支持spill to tempdb,导致无法承载太大的结果集。
2.该算法需要遍历N次,如果N过大,则成本过高。
对于SQL Server来说,这个N是否过大的阈值是100。下面我们来看一个例子,测试数据和代码如代码清单1所示。
CREATE TABLE TestTop
(id INT,sortkey INT,SOMEvalue CHAR(1000))
DECLARE @i INT =1
WHILE @i<300000
BEGIN
INSERT INTO TestTop VALUES(@i,@i,'a')
SET @i=@i+1
END
CREATE CLUSTERED INDEX PK_id ON TestTop(id)
--test 1
SELECT TOP(100) * FROM TestTop
ORDER BY sortkey
--test 2
SELECT TOP(101) * FROM TestTop
ORDER BY sortkey
代码清单1.测试数据与测试代码
第一个测试为TOP 100,正好使用TOP N Sort的算法,第二个测试为TOP 101,只能使用普通Sort的算法,如图2所示。

图2.TOP 101的SORT需要更多内存,从而导致内存授予不足spill to tempdb
我们再来看执行时间,由于spill to tempdb的存在,那么执行时间如图3所示。

图3.相差非常大的执行时间
从图3可以看出,执行时间相差非常大。
因此对于TOP的使用来说,尽量使用TOP 100以内的数值。
多表情况
由于TOP语句带有对优化器基数估计的提示功能,因此多表查询时在极端情况下可能导致行数低估从而影响性能。
比如下面如图4的示例查询

图4.使用TOP 1的表接连查询
在这种情况下,由于TOP1的存在使得查询优化器使用1作为估计行数,与实际的行数差异巨大,因此对于这种情况,使用TOP反而可能导致成本更高(虽然我们看到图4中估计的是0%对比100%,但实际差异巨大),更高的原因不仅仅是优化器估计为1,因为Loop Join只要发现1条就可以立刻结束,但上面例子中由于过滤条件选择性过低,导致找到第一条数据的随机查找过多(loop join内表循环是随机IO),成本如图5所示。

图5.使用TOP反而导致性能下降
根本原因是由于估计行数只有1行,大部分情况下这一行
对于上面这种情况来说,我们通常可以有下面集中解决办法:
1.使用提示,由于我们知道这是由于实际行数远大于估计行数导致,因此我们可以尝试使用hash join,forcescan等提示。
2.增加where条件,使得返回行数具有更高的选择性。
3.不使用TOP1,而使用TOP 10以上的数字,让估计行数变大,比如图5中的查询我们由TOP1 变为TOP10,那么执行计划则变为如图6所示。

图6.TOP 10的执行计划
这是由于当行数少时,LOOP JOIN可以更快返回有限的行数,相当于对表加了FAST N提示,但行数增多时,优化器更倾向使用MERGE或者HASH完成操作,在上面返回行极多(选择性低)的极端情况下,会拥有更好的性能,结果如图7所示。

图7.特殊情况下TOP10相比TOP1有更好性能。
因此结合单表的例子,推荐使用TOP关键字时,数字在10到100之间。
小结
本文介绍了TOP关键字在单表和多表条件下可能对执行计划产生的影响,进而影响了查询计划。TOP影响执行计划主要是下面两个方面:
- 内存授予
- 估计行数
因此在特殊情况下调优TOP语句时,可以根据实际情况考虑本文的建议。
SQL Server中TOP子句可能导致的问题以及解决办法的更多相关文章
- SQL SERVER中什么情况会导致索引查找变成索引扫描
SQL Server 中什么情况会导致其执行计划从索引查找(Index Seek)变成索引扫描(Index Scan)呢? 下面从几个方面结合上下文具体场景做了下测试.总结.归纳. 1:隐式转换会导致 ...
- SQL Server数据库实例名与服务器名不一致的解决办法
SQL Server数据库实例名与服务器名不一致的解决办法 --EXEC sp_addlinkedserver -- @server = 'PSHGQ' --GO --select * from ...
- SQL Server中提前找到隐式转换提升性能的办法
http://www.cnblogs.com/shanksgao/p/4254942.html 高兄这篇文章很好的谈论了由于数据隐式转换造成执行计划不准确,从而造成了死锁.那如果在事情出现之前 ...
- SQL SERVER中关于OR会导致索引扫描或全表扫描的浅析
在SQL SERVER的查询语句中使用OR是否会导致不走索引查找(Index Seek)或索引失效(堆表走全表扫描 (Table Scan).聚集索引表走聚集索引扫描(Clustered Index ...
- SQL SERVER中关于OR会导致索引扫描或全表扫描的浅析 (转载)
在SQL SERVER的查询语句中使用OR是否会导致不走索引查找(Index Seek)或索引失效(堆表走全表扫描 (Table Scan).聚集索引表走聚集索引扫描(Clustered Index ...
- SQL Server没有足够的内存继续执行程序 (mscorlib)的解决办法
在Microsoft SQL Server Management Studio 中执行较大的sql脚本时,会报没有足够的内存继续执行程序(mscorlib)的错误.如下图所示 解决方法: 使用sqlc ...
- MS SQL SERVER 2008 R2 实例服务启动出现10048错误解决办法
由于个人癖好,把MSSQLSERVER服务禁止了开机启动,每次需要的时候就输入CMD命令开启.今天在开启的时候,系统提示“发生服务特定错误:10048”. 于是打开SQL Server配置管理器,发现 ...
- SQL Server 没有足够的内存继续执行程序 (mscorlib)的解决办法
问题: SQL Server 执行大脚本时,出现如下提示信息: 解决方法: 使用 sqlcmd.exe 导入 1.win + r,输入 cmd 进入 C:\Program Files\Microsof ...
- Windows 2012 安装 SQL Server 2012,.Net Framework 3.5安装不成的解决办法
This behavior can also be caused by a system administrator who configures the computer to use Window ...
随机推荐
- 彻底理解AC多模式匹配算法
(本文尤其适合遍览网上的讲解而仍百思不得姐的同学) 一.原理 AC自动机首先将模式组记录为Trie字典树的形式,以节点表示不同状态,边上标以字母表中的字符,表示状态的转移.根节点状态记为0状态,表示起 ...
- PowerDesigner-VBSrcipt-自动设置主键,外键名等(SQL Server)
在PowerDesigner中的设计SQL Server 数据表时,要求通过vbScript脚本实现下面的功能: 主键:pk_TableName 外键:fk_TableName_ForeignKeyC ...
- Matlab 高斯_拉普拉斯滤波器处理医学图像
前言:本程序是我去年实现论文算法时所做.主要功能为标记切割肝脏区域.时间有点久,很多细节已经模糊加上代码做了很多注释,因此在博客中不再详述. NOTE: 程序分几大段功能模块,仔细阅读,对解决医学图像 ...
- 结巴分词3--基于汉字成词能力的HMM模型识别未登录词
作者:zhbzz2007 出处:http://www.cnblogs.com/zhbzz2007 欢迎转载,也请保留这段声明.谢谢! 1 算法简介 在 结巴分词2--基于前缀词典及动态规划实现分词 博 ...
- EventBus实现activity跟fragment交互数据
最近老是听到技术群里面有人提出需求,activity跟fragment交互数据,或者从一个activity跳转到另外一个activity的fragment,所以我给大家介绍一个开源项目,EventBu ...
- PHP的学习--RSA加密解密
PHP服务端与客户端交互或者提供开放API时,通常需要对敏感的数据进行加密,这时候rsa非对称加密就能派上用处了. 举个通俗易懂的例子,假设我们再登录一个网站,发送账号和密码,请求被拦截了. 密码没加 ...
- C#使用Aspose.Cells导出Excel简单实现
首先,需要添加引用Aspose.Cells.dll,官网下载地址:http://downloads.aspose.com/cells/net 将DataTable导出Xlsx格式的文件下载(网页输出) ...
- (资源整理)带你入门Spark
一.Spark简介: 以下是百度百科对Spark的介绍: Spark 是一种与 Hadoop 相似的开源集群计算环境,但是两者之间还存在一些不同之处,这些有用的不同之处使 Spark 在某些工作负载方 ...
- 换个角度看微信小程序[推荐]
去年参加几次技术沙龙时,我注意到一个有意思的现象:与之前大家统一接受的换名片不同,有些人并不愿意被添加微信好友--"不好意思,不熟的人不加微信". 这个现象之所以有意思,是因为名片 ...
- MONO x64 amd_x64
SharpPcap 是一个.NET 环境下的网络包捕获框架,基于著名的 pcap/WinPcap 库开发.提供了捕获.注入.分析和构建的功能,SharpPcap 还是一个跨平台的库,可以在Mono/. ...