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

  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"欺骗"案例

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

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

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

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

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

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

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

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

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

  9. SQL Server控制执行计划

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

随机推荐

  1. C# 在Word文档中生成条形码

    C# 在Word文档中生成条形码 简介 条形码是由多个不同的空白和黑条按照一定的顺序组成,用于表示各种信息如产品名称.制造商.类别.价格等.目前,条形码在我们的日常生活中有着很广泛的应用,不管是在图书 ...

  2. 一张图看懂normal,static,sealed,abstract 的 区别

    +-------------------------+---+--------+--------+--------+----------+ | Class Type | | normal | stat ...

  3. c#知识点总结

    1.如果要使用自动属性的话,必须2个都是自动属性, 不允许出现一个自动,一个非自动的情况,否则会报错. 2.命名规则,最好用动词+名词 比如 Is+Member+Valid ,方法的首字母大写,变量的 ...

  4. spring boot启用tomcat ssl

    首先要生成一个keystore证书.参考:Tomcat创建HTTPS访问,java访问https,ssl证书生成:cer&jks文件生成摘录,spring-boot 这里复现一下完整过程: 安 ...

  5. Java中的访问控制权限

    简介 Java中为什么要设计访问权限控制机制呢?主要作用有两点: (1)为了使用户不要触碰那些他们不该触碰的部分,这些部分对于类内部的操作时必要的,但是它并不属于客户端程序员所需接口的一部分. (2) ...

  6. js的stopPropagation()、cancelBubble、preventDefault()、return false的分析

    个人笔记,如有错误,望指出. 事件冒泡,举个列子: <li> <a href='http://www.baidu.com'>点击a</a> </li> ...

  7. 纯C#实现屏幕指定区域截屏

    以前在别的地方见过一个通过调用系统API实现屏幕截图的例子,从内心来说我不太喜欢在C#代码中出现这种情况,现在什么都讲“和谐”,我觉得这种做法就是破坏了我们的“和谐”代码,呵呵,开玩笑,有的时候,不通 ...

  8. html5手机端的点击弹出侧边滑动菜单代码

    效果预览:http://hovertree.com/texiao/html5/19/ 本效果适用于移动设备,可以使用手机等浏览效果. 源码下载:http://hovertree.com/h/bjaf/ ...

  9. case break结构与return的有关要点

    //确认事件 private void cmd_ok_Click(object sender, EventArgs e) { //客户名称是否为空 if (txt_banhao.Text.TrimEn ...

  10. 解决EP拒绝访问注册表Global键的的问题

    问题描述   打开EP站点时出现如下Error: Message: An unhandled error has occurred. To view details about this error, ...