开发人员遇到一个及其诡异的的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. ZOJ Problem Set - 1402 Magnificent Meatballs

    比较简单的题目,题目大意就是将n个数字围成一个圈,找到一个划分,是的划分左边的数字之和等于右边的数字之和: e.g 10 1 2 2 5,那么可以找到一个划分10 | 1 2 2 5使得两边数字之和都 ...

  2. Ionic2学习笔记(5):Provider

    作者:Grey 原文地址: http://www.cnblogs.com/greyzeng/p/5547646.html             Provider是一种为App提供数据源的方式, 举个 ...

  3. Effective java笔记(六),方法

    38.检查参数的有效性 绝大多数方法和构造器对于传递给它们的参数值都会有限制.如,对象引用不能为null,数组索引有范围限制等.应该在文档中指明所有这些限制,并在方法的开头处检查参数,以强制施加这些限 ...

  4. [Tool] csdn客户端开发(非官方版)

    偶尔间看到一篇博客[清山博客]里讲述了他自己开发的一个CSDN博客客户端,并去下载体验了一下,然后就自己手痒也要开发一下 先看看结果图: 在文章列表里,鼠标右键可以操作[置顶.删除.评论权限]: 下面 ...

  5. linux使用心得(持续更新)

    ! 查看发行版本信息 lsb_release -a uname -a   以下方法只适合redhat和centos cat /etc/redhat-release rpm -q redhat-rele ...

  6. 服务器Config文件不能查看的问题

      由于某种需求,需要从IIS发布的服务中下载扩展名为config的文件,但是发布文件后,在浏览器无法查看文件.根据反馈的的错误提示,大致说config属于配置文件,处于安全考虑,不能随便浏览. 如果 ...

  7. Sql server函数的学习1(系统变量、错误函数、转换函数)

    一.系统变量的介绍和使用 1.@@ERROR 变量 2.@@SERVICENAME 变量 3.@@TOTAL_ERRORS 变量 4.@@TOTAL_READ 变量 5.@@VERSION 变量 二. ...

  8. Unity3D 5.x 简单实例 - 脚本编写

    1,Vector3 类型变量存储向量坐标值 Vector3.forward Vector3(0,0,1) Vector3.up Vector3(0,1,0) Vector3.right Vector3 ...

  9. Cats(4)- 叠加Free程序运算结果,Stacking monadic result types

    在前面的几篇关于Free编程的讨论示范中我们均使用了基础类型的运算结果.但在实际应用中因为需要考虑运算中出现异常的情况,常常会需要到更高阶复杂的运算结果类型如Option.Xor等.因为Monad无法 ...

  10. HttpClient在HTTP协议接口测试中的使用

    TTP协议的接口测试中,使用到最多的就是GET请求与POST请求,其中POST请求有FORM参数提交请求与RAW请求,下面我将结合HttpClient来实现一下这三种形式: 一.GET请求: GET请 ...