SQL SERVER 2012 执行计划走嵌套循环导致性能问题的案例

时间:2022-04-08 21:49:55

开发人员遇到一个及其诡异的的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 SERVER 2012 执行计划走嵌套循环导致性能问题的案例

执行上面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 SERVER 2012 执行计划走嵌套循环导致性能问题的案例

我姑且将执行性能较好的SQL的执行计划叫做Plan A,执行性能很差的SQL的执行计划叫做Plan B

Plan A

SQL SERVER 2012 执行计划走嵌套循环导致性能问题的案例

Plan B

SQL SERVER 2012 执行计划走嵌套循环导致性能问题的案例

如上所示,Plan B 看似开销都耗费在键查找那一块,但是如果查看具体信息(如下所示),并无特别地方。

SQL SERVER 2012 执行计划走嵌套循环导致性能问题的案例

于是我使用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')

SQL SERVER 2012 执行计划走嵌套循环导致性能问题的案例

于是我将怀疑的地方转移到表连接方式,使用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 执行计划走嵌套循环导致性能问题的案例的更多相关文章

  1. SQL Server 优化-执行计划

    对于SQL Server的优化来说,优化查询可能是很常见的事情.由于数据库的优化,本身也是一个涉及面比较的广的话题, 因此本文只谈优化查询时如何看懂SQL Server查询计划.毕竟我对SQL Ser ...

  2. 了解Sql Server的执行计划

    前一篇总结了Sql Server Profiler,它主要用来监控数据库,并跟踪生成的sql语句.但是只拿到生成的sql语句没有什么用,我们可以利用这些sql语句,然后结合执行计划来分析sql语句的性 ...

  3. SQL SERVER 2012数据库:开启防火墙导致外部无法连接数据库解决办法

    SQL SERVER 2012数据库:开启防火墙导致外部无法连接数据库解决办法 将以下代码存为OpenSqlServerPort.bat文件: netsh advfirewall firewall a ...

  4. SQL Server实际执行计划COST&quot&semi;欺骗&quot&semi;案例

    有个系统,昨天Support人员发布了相关升级脚本后,今天发现系统中有个功能不能正常使用了,直接报超时了(Timeout expired)的错误.定位到相关相关存储过程后,然后在优化分析的过程中,又遇 ...

  5. 程序员眼中的 SQL Server-执行计划教会我如何创建索引?

    先说点废话 以前有 DBA 在身边的时候,从来不曾考虑过数据库性能的问题,但是,当一个应用程序从头到脚都由自己完成,而且数据库面对的是接近百万的数据,看着一个页面加载速度像乌龟一样,自己心里真是有种挫 ...

  6. SQL Server-执行计划教会我如何创建索引

    先说点废话 以前有 DBA 在身边的时候,从来不曾考虑过数据库性能的问题,但是,当一个应用程序从头到脚都由自己完成,而且数据库面对的是接近百万的数据,看着一个页面加载速度像乌龟一样,自己心里真是有种挫 ...

  7. Sql Server中执行计划的缓存机制

    Sql查询过程 当执行一个Sql语句或者存储过程时, Sql Server的大致过程是 1. 对查询语句进行分析,将其生成逻辑单元,并进行基本的语法检查 2. 生成查询树(会将查询语句中所有操作转换为 ...

  8. Win8&period;1 IIS6 SQL SERVER 2012 执行 SqlServices&period;InstallSessionState 出错

    新装了WIN8.1,感觉很不错. 新建了第一个站点是,在执行 SqlServices.InstallSessionState("localhost", null, SessionS ...

  9. SQL Server控制执行计划

    为了提高性能,可以使用提示(hints)特性,包含以下三类: 查询提示:(query hints)告知优化器在整个查询过程中都应用某个提示 关联提示:(join hints)告知优化器在查询的特定部分 ...

随机推荐

  1. 现代软件工程作业 github使用

    Github使用 版本库的创建与同步 第一步:创建远程版本库并同步到本地 创建远程版本库 在地址栏输入www.github.com 并sign in 进入到个人主页,如下图示: 创建远程版本库:点击N ...

  2. mysql&lowbar;fetch&lowbar;row,mysql&lowbar;fetch&lowbar;array,mysql&lowbar;fetch&lowbar;object,mysql&lowbar;fetch&lowbar;assoc的区别!

    php从mysql中访问数据库并取得数据,取得结果的过程中用到好几个类似的方法,区别及用法值得区分一下,看下面的代码   代码如下: <?php $link=mysql_connect('loc ...

  3. java如何从方法返回多个值

    本文介绍三个方法,使java方法返回多个值. 方法1:使用集合类 方法2:使用封装对象 方法3:使用引用传递 示例代码如下: import java.util.HashMap; import java ...

  4. 使用Xib添加自定义View

    1.新建Cocoa Touch Class以及UI View,2者同名 2.设置UI View的File's Owner——Custom Class为之前新建类 3.设置Xib中View与类关联 4. ...

  5. ListView属性解释

    1.android:scrollbarStyle 定义滚动条的样式和位置 参考:http://www.trinea.cn/android/android-scrollbarstyle/ 2.andro ...

  6. 201521123039 《java程序设计》第十二周学习总结

    1. 本周学习总结 2. 书面作业 将Student对象(属性:int id, String name,int age,double grade)写入文件student.data.从文件读出显示. 字 ...

  7. execl列数据成等差递增递减

    如上图若想以10,20,30...这样递增: 1).首先需选中10,20所在的单元格,鼠标移至20所在的单元格右下角 2).此时会出现一个十字"十"符号,点击直向下拖动至某个地方, ...

  8. Spring 捕捉校验参数异常并统一处理

    使用 @Validated ,@Valid ,@NotBlank 之类的,请自行百度,本文着重与捕捉校验失败信息并封装返回出去 参考: https://mp.weixin.qq.com/s/EaZxY ...

  9. Confluence 6 配置快速导航

    当在 Confluence 中的快速导航进行查找的时候(请查看 Searching Confluence)能够帮助你显示页面下拉列表和其他的项目,这个是通过查找页面标题进行比对的.在默认情况下,这个功 ...

  10. MVC3学习:利用mvc3&plus;ajax实现全选和批量删除

    本例数据库操作使用EF code first; 先利用mvc自带的模板,先生成一个list视图,然后再手动添加复选框和删除按钮 <table> <tr> @*在标题行添加一个全 ...