SQL SERVER 2012 执行计划走嵌套循环导致性能问题的案例
开发人员遇到一个及其诡异的的SQL性能问题,这段完整SQL语句如下所示:
declare @UserId INT
declare @PSANo VARCHAR(200)
declare @ShipMode VARCHAR(10)
declare @CY_FLAG VARCHAR(1)
declare @PO VARCHAR(20)
declare @BuyerName VARCHAR(100)
declare @Destination VARCHAR(1)
declare @FinalDestination VARCHAR(40)
declare @Factory VARCHAR(10)
declare @NoticeDateStart DATETIME
declare @NoticeDateEnd DATETIME
declare @EELForwarder VARCHAR(100)
declare @SortExpression VARCHAR(100)
declare @RowIndex INT
declare @PageSize INT
declare @ExistNoticeKey varchar(200)
DECLARE @NULLDATE DATETIME
SET @NULLDATE=GETDATE()
set @UserId=39
set @PSANo=''
set @ShipMode=''
set @CY_FLAG=''
set @PO=N''
set @BuyerName=N''
set @Destination=N''
set @FinalDestination=N''
set @Factory=''
set @EELForwarder=N''
set @SortExpression=''
set @RowIndex=0
set @PageSize=10
set @ExistNoticeKey=''
DECLARE @CountSql NVARCHAR(max)
DECLARE @DataSql NVARCHAR(max)
declare @next int
declare @Where_PSANo varchar(400)
declare @Index_PSANo varchar(40)
declare @Where_ExcludeNotcekey varchar(400)
set @Where_PSANo=''
SET NOCOUNT ON;
set @next=1
while @next<=dbo.Get_StrArrayLength(@PSANo,',')
begin
set @Index_PSANo = dbo.Get_StrArrayStrOfIndex(@PSANo,',',@next)
set @Where_PSANo = @Where_PSANo + ' Or notice.PSA_NO LIKE ''%'+@Index_PSANo+'%'''
set @next=@next+1
end
set @Where_ExcludeNotcekey=''
if @ExistNoticeKey!=''
begin
set @Where_ExcludeNotcekey=' or notice.NOTICE_KEY not in('+ @ExistNoticeKey+')';
--select @Where_ExcludePSANo
--print 'OK'
end
SELECT SUM(ISNULL(FactQty,0)) AS FactQty, NOTICE_KEY INTO #TEMP
FROM
(
SELECT A.NOTICE_KEY,SUM(ISNULL(A.FactQty,0)) FactQty FROM IES.InvoiceFourLine A GROUP BY A.NOTICE_KEY
UNION ALL
SELECT A.NoticeKey AS NOTICE_KEY,SUM(ISNULL(A.FactQty,0)) FactQty FROM IES.InvoiceThreeByrFwdChargeLine A GROUP BY A.NoticeKey
) T GROUP BY NOTICE_KEY
SELECT COUNT(*)
FROM IES.ExportNotice notice --WITH (INDEX(PK_EXPORTNOTICE))
LEFT JOIN #TEMP t ON notice.NOTICE_KEY = T.NOTICE_KEY
WHERE
notice.FACTORY_CD IN(SELECT SiteId FROM DCL.SecurityUserSiteMapping WHERE UserId=39)
AND (ISNULL(notice.FACT_EXPORT_QTY,0)-ISNULL(T.FactQty,0))>0
AND (ISNULL(@PSANo,'')='' Or notice.PSA_NO LIKE '%%')
AND (ISNULL(@ExistNoticeKey,'')='' )
AND (ISNULL(@ShipMode,'')='' OR notice.SHIP_MODE_CD=@ShipMode)
AND (ISNULL(@CY_FLAG,'')='' OR notice.CY_FLAG=@CY_FLAG)
AND (ISNULL(@PO,'')='' OR notice.BUYER_PO_NO LIKE '%'+@PO+'%')
AND (ISNULL(@BuyerName,'')='' OR notice.NAME LIKE '%'+@BuyerName+'%')
AND (ISNULL(@Destination,'')='' OR notice.SZ=@Destination)
AND (ISNULL(@FinalDestination,'')='' OR notice.FINAL_DESTINATION LIKE '%'+@FinalDestination+'%')
AND (ISNULL(@Factory,'')='' OR notice.FACTORY_CD=@Factory)
AND (ISNULL(@EELForwarder,'')='' OR notice.EEL_FORWARDER=@EELForwarder)
AND (ISNULL(@NoticeDateStart,'2000-01-01')='2000-01-01')
---AND ( ISNULL(@NoticeDateEnd,'1999-01-01')='1999-01-01')
DROP TABLE #TEMP
案例的环境为SQL SERVER 2012 Standard Edition (64-bit),具体版本号为11.0.5058.0 ,另外表IES.ExportNotice的数据记录为2万多。表IES.InvoiceThreeByrFwdChargeLine的记录数为1万多,表IES.InvoiceFourLine的记录只有区区几十条。临时表 #TEMP的记录为1万多条。
执行上面SQL语句一般一秒以内完成。但是这段SQL如果将最后注释的条件加上(也就是最后注释的语句取消注释)
SELECT COUNT(*)
FROM IES.ExportNotice notice --WITH (INDEX(PK_EXPORTNOTICE))
LEFT JOIN #TEMP t ON notice.NOTICE_KEY = T.NOTICE_KEY
WHERE
notice.FACTORY_CD IN(SELECT SiteId FROM DCL.SecurityUserSiteMapping WHERE UserId=39)
AND (ISNULL(notice.FACT_EXPORT_QTY,0)-ISNULL(T.FactQty,0))>0
AND (ISNULL(@PSANo,'')='' Or notice.PSA_NO LIKE '%%')
AND (ISNULL(@ExistNoticeKey,'')='' )
AND (ISNULL(@ShipMode,'')='' OR notice.SHIP_MODE_CD=@ShipMode)
AND (ISNULL(@CY_FLAG,'')='' OR notice.CY_FLAG=@CY_FLAG)
AND (ISNULL(@PO,'')='' OR notice.BUYER_PO_NO LIKE '%'+@PO+'%')
AND (ISNULL(@BuyerName,'')='' OR notice.NAME LIKE '%'+@BuyerName+'%')
AND (ISNULL(@Destination,'')='' OR notice.SZ=@Destination)
AND (ISNULL(@FinalDestination,'')='' OR notice.FINAL_DESTINATION LIKE '%'+@FinalDestination+'%')
AND (ISNULL(@Factory,'')='' OR notice.FACTORY_CD=@Factory)
AND (ISNULL(@EELForwarder,'')='' OR notice.EEL_FORWARDER=@EELForwarder)
AND (ISNULL(@NoticeDateStart,'2000-01-01')='2000-01-01')
AND ( ISNULL(@NoticeDateEnd,'1999-01-01')='1999-01-01')
然后执行时发现SQL慢得令人发指,非常的不可以思议。 如果按照我们理解,这个条件( ISNULL(@NoticeDateEnd,'1999-01-01')='1999-01-01') 仅仅相当于一个 1=1 或1=0的条件,怎么会有如此大的性能差距呢? 查看执行计划后,发现加上这样一个条件后,执行计划完全不同了。
我姑且将执行性能较好的SQL的执行计划叫做Plan A,执行性能很差的SQL的执行计划叫做Plan B
Plan A
Plan B
如上所示,Plan B 看似开销都耗费在键查找那一块,但是如果查看具体信息(如下所示),并无特别地方。
于是我使用HINT,强制在表IES.ExportNotice上走索引PK_EXPORTNOTICE,结果发现执行时,执行速度依然慢的令人发指。我觉得执行计划有些问题,Cost可能并不正确。
SELECT COUNT(*)
FROM IES.ExportNotice notice WITH (INDEX(PK_EXPORTNOTICE))
LEFT JOIN #TEMP t ON notice.NOTICE_KEY = T.NOTICE_KEY
WHERE
notice.FACTORY_CD IN(SELECT SiteId FROM DCL.SecurityUserSiteMapping WHERE UserId=39)
AND (ISNULL(notice.FACT_EXPORT_QTY,0)-ISNULL(T.FactQty,0))>0
AND (ISNULL(@PSANo,'')='' Or notice.PSA_NO LIKE '%%')
AND (ISNULL(@ExistNoticeKey,'')='' )
AND (ISNULL(@ShipMode,'')='' OR notice.SHIP_MODE_CD=@ShipMode)
AND (ISNULL(@CY_FLAG,'')='' OR notice.CY_FLAG=@CY_FLAG)
AND (ISNULL(@PO,'')='' OR notice.BUYER_PO_NO LIKE '%'+@PO+'%')
AND (ISNULL(@BuyerName,'')='' OR notice.NAME LIKE '%'+@BuyerName+'%')
AND (ISNULL(@Destination,'')='' OR notice.SZ=@Destination)
AND (ISNULL(@FinalDestination,'')='' OR notice.FINAL_DESTINATION LIKE '%'+@FinalDestination+'%')
AND (ISNULL(@Factory,'')='' OR notice.FACTORY_CD=@Factory)
AND (ISNULL(@EELForwarder,'')='' OR notice.EEL_FORWARDER=@EELForwarder)
AND (ISNULL(@NoticeDateStart,'2000-01-01')='2000-01-01')
AND ( ISNULL(@NoticeDateEnd,'1999-01-01')='1999-01-01')
于是我将怀疑的地方转移到表连接方式,使用Table HINT,强制下面SQL语句走HASH JOIN,结果SQL一秒钟执行完成。
SELECT COUNT(*)
FROM IES.ExportNotice notice
LEFT HASH JOIN #TEMP t ON notice.NOTICE_KEY = T.NOTICE_KEY
WHERE
notice.FACTORY_CD IN(SELECT SiteId FROM DCL.SecurityUserSiteMapping WHERE UserId=39)
AND (ISNULL(notice.FACT_EXPORT_QTY,0)-ISNULL(T.FactQty,0))>0
AND (ISNULL(@PSANo,'')='' Or notice.PSA_NO LIKE '%%')
AND (ISNULL(@ExistNoticeKey,'')='' )
AND (ISNULL(@ShipMode,'')='' OR notice.SHIP_MODE_CD=@ShipMode)
AND (ISNULL(@CY_FLAG,'')='' OR notice.CY_FLAG=@CY_FLAG)
AND (ISNULL(@PO,'')='' OR notice.BUYER_PO_NO LIKE '%'+@PO+'%')
AND (ISNULL(@BuyerName,'')='' OR notice.NAME LIKE '%'+@BuyerName+'%')
AND (ISNULL(@Destination,'')='' OR notice.SZ=@Destination)
AND (ISNULL(@FinalDestination,'')='' OR notice.FINAL_DESTINATION LIKE '%'+@FinalDestination+'%')
AND (ISNULL(@Factory,'')='' OR notice.FACTORY_CD=@Factory)
AND (ISNULL(@EELForwarder,'')='' OR notice.EEL_FORWARDER=@EELForwarder)
AND (ISNULL(@NoticeDateStart,'2000-01-01')='2000-01-01')
AND ( ISNULL(@NoticeDateEnd,'1999-01-01')='1999-01-01')
虽然解决了问题,但是我隐隐觉得这应该是SQL SERVER优化器的某些Bug才导致出现这种特殊的情况。而且执行计划的Cost也完全不准确。让人有点匪夷所思。
SQL SERVER 2012 执行计划走嵌套循环导致性能问题的案例的更多相关文章
- SQL Server 优化-执行计划
对于SQL Server的优化来说,优化查询可能是很常见的事情.由于数据库的优化,本身也是一个涉及面比较的广的话题, 因此本文只谈优化查询时如何看懂SQL Server查询计划.毕竟我对SQL Ser ...
- 了解Sql Server的执行计划
前一篇总结了Sql Server Profiler,它主要用来监控数据库,并跟踪生成的sql语句.但是只拿到生成的sql语句没有什么用,我们可以利用这些sql语句,然后结合执行计划来分析sql语句的性 ...
- SQL SERVER 2012数据库:开启防火墙导致外部无法连接数据库解决办法
SQL SERVER 2012数据库:开启防火墙导致外部无法连接数据库解决办法 将以下代码存为OpenSqlServerPort.bat文件: netsh advfirewall firewall a ...
- SQL Server实际执行计划COST"欺骗"案例
有个系统,昨天Support人员发布了相关升级脚本后,今天发现系统中有个功能不能正常使用了,直接报超时了(Timeout expired)的错误.定位到相关相关存储过程后,然后在优化分析的过程中,又遇 ...
- 程序员眼中的 SQL Server-执行计划教会我如何创建索引?
先说点废话 以前有 DBA 在身边的时候,从来不曾考虑过数据库性能的问题,但是,当一个应用程序从头到脚都由自己完成,而且数据库面对的是接近百万的数据,看着一个页面加载速度像乌龟一样,自己心里真是有种挫 ...
- SQL Server-执行计划教会我如何创建索引
先说点废话 以前有 DBA 在身边的时候,从来不曾考虑过数据库性能的问题,但是,当一个应用程序从头到脚都由自己完成,而且数据库面对的是接近百万的数据,看着一个页面加载速度像乌龟一样,自己心里真是有种挫 ...
- Sql Server中执行计划的缓存机制
Sql查询过程 当执行一个Sql语句或者存储过程时, Sql Server的大致过程是 1. 对查询语句进行分析,将其生成逻辑单元,并进行基本的语法检查 2. 生成查询树(会将查询语句中所有操作转换为 ...
- Win8.1 IIS6 SQL SERVER 2012 执行 SqlServices.InstallSessionState 出错
新装了WIN8.1,感觉很不错. 新建了第一个站点是,在执行 SqlServices.InstallSessionState("localhost", null, SessionS ...
- SQL Server控制执行计划
为了提高性能,可以使用提示(hints)特性,包含以下三类: 查询提示:(query hints)告知优化器在整个查询过程中都应用某个提示 关联提示:(join hints)告知优化器在查询的特定部分 ...
随机推荐
- ZOJ Problem Set - 1402 Magnificent Meatballs
比较简单的题目,题目大意就是将n个数字围成一个圈,找到一个划分,是的划分左边的数字之和等于右边的数字之和: e.g 10 1 2 2 5,那么可以找到一个划分10 | 1 2 2 5使得两边数字之和都 ...
- Ionic2学习笔记(5):Provider
作者:Grey 原文地址: http://www.cnblogs.com/greyzeng/p/5547646.html Provider是一种为App提供数据源的方式, 举个 ...
- Effective java笔记(六),方法
38.检查参数的有效性 绝大多数方法和构造器对于传递给它们的参数值都会有限制.如,对象引用不能为null,数组索引有范围限制等.应该在文档中指明所有这些限制,并在方法的开头处检查参数,以强制施加这些限 ...
- [Tool] csdn客户端开发(非官方版)
偶尔间看到一篇博客[清山博客]里讲述了他自己开发的一个CSDN博客客户端,并去下载体验了一下,然后就自己手痒也要开发一下 先看看结果图: 在文章列表里,鼠标右键可以操作[置顶.删除.评论权限]: 下面 ...
- linux使用心得(持续更新)
! 查看发行版本信息 lsb_release -a uname -a 以下方法只适合redhat和centos cat /etc/redhat-release rpm -q redhat-rele ...
- 服务器Config文件不能查看的问题
由于某种需求,需要从IIS发布的服务中下载扩展名为config的文件,但是发布文件后,在浏览器无法查看文件.根据反馈的的错误提示,大致说config属于配置文件,处于安全考虑,不能随便浏览. 如果 ...
- Sql server函数的学习1(系统变量、错误函数、转换函数)
一.系统变量的介绍和使用 1.@@ERROR 变量 2.@@SERVICENAME 变量 3.@@TOTAL_ERRORS 变量 4.@@TOTAL_READ 变量 5.@@VERSION 变量 二. ...
- Unity3D 5.x 简单实例 - 脚本编写
1,Vector3 类型变量存储向量坐标值 Vector3.forward Vector3(0,0,1) Vector3.up Vector3(0,1,0) Vector3.right Vector3 ...
- Cats(4)- 叠加Free程序运算结果,Stacking monadic result types
在前面的几篇关于Free编程的讨论示范中我们均使用了基础类型的运算结果.但在实际应用中因为需要考虑运算中出现异常的情况,常常会需要到更高阶复杂的运算结果类型如Option.Xor等.因为Monad无法 ...
- HttpClient在HTTP协议接口测试中的使用
TTP协议的接口测试中,使用到最多的就是GET请求与POST请求,其中POST请求有FORM参数提交请求与RAW请求,下面我将结合HttpClient来实现一下这三种形式: 一.GET请求: GET请 ...





