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)告知优化器在查询的特定部分 ...
随机推荐
- swift中Range的使用书名
在swift中Range有两种用法 1.把字符串转换成NSString来使用 //这里是把swift的字符换转换成了nsstring 使用 let str :NSString = text.strin ...
- mouseenter以及mouseleave兼容性
在IE的全系列中都实现了mouseenter和mouseleave事件,但是在早期的w3c浏览器中却没有实现这两个事件.有时候,我们需要使用 mouseenter事件来防止子元素的冒泡,这就涉及到事件 ...
- Android 学习Activity(1)activity和intent
工具是:JDK环境配置+SDK+ADT工具 一.Activity 主要作用: 1.用户与应用程序的接口 2.控件的容器 二.创建Activity要点:(在src中的目录下包里) 1.一个Activi ...
- 学习Linux下s3c2440的USB鼠标驱动笔记
1.ARM-Linux下USB驱动程序开发1.1.1.linux下USB配置:*********(MassStorage:存储设备)********************************** ...
- 简析Geoserver中获取图层列表以及各图层描述信息的三种方法
文章版权由作者李晓晖和博客园共有,若转载请于明显处标明出处:http://www.cnblogs.com/naaoveGIS/. 1.背景 实际项目中需要获取到Geoserver中的图层组织以及各图层 ...
- 导入android-support-v4.jar的方法
在导入使用了ViewPage,ActionBar,Fragment的工程后出现错误,很有可能是没有导入4.0版本的支持包. 正确导入方法为: 首先在Project->properties-> ...
- 在IIS下部署Thinkphp项目,验证码不能显示的解决办法
由于公司租用的是虚拟空间,而且用的是IIS服务器,所以部署PHP的时候就出现很多问题:比如昨天就碰到这个问题:在IIS下部署Thinkphp项目,验证码不能显示 这是生成验证码的方法: // 制作专门 ...
- MongoDB-基础-条件操作符
1.一些解释 less than : 比..少 lt greater than : 比..多 gt equals :相等 e ...
- Apworks框架实战(二):开始使用
要使用Apworks进行应用程序开发,您需要安装Visual Studio 2012以上的版本(以下简称Visual Studio 2012+),从Apworks 2.5开始,需要Micrsoft.N ...
- react基础(1)
在 react入门系列 里面,介绍了一些react的基础知识,在react基础部分,会结合手脚架工具进行更多的总结. 关于webpack我在这里就不讲解了,有需要的小伙伴可以自己去百度一下学习资料,我 ...





