开发人员遇到一个及其诡异的的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性能很重要? (Why is query execution important for SQL Server performance?) SQL Server性能 ...

  2. sql查询初学者指南_面向初学者SQL Server查询执行计划–类型和选项

    sql查询初学者指南 When a DBA is working with the SQL Server, he/she might sometimes say that the execution ...

  3. 了解Sql Server的执行计划

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

  4. sql查询初学者指南_面向初学者SQL Server查询执行计划–聚集索引运算符

    sql查询初学者指南 We have discussed how to created estimated execution plans and actual execution plans in ...

  5. sql查询初学者指南_面向初学者SQL Server查询执行计划–非聚集索引运算符

    sql查询初学者指南 Now that we understand what Clustered Index Scan and Clustered Index Seek are, how they o ...

  6. 面向初学者的 SQL Server 查询执行计划(1)——聚集索引运算符(Clustered Index)

    在本文中,我们将讨论与聚集索引相关的各种执行计划运算符,以及它们的作用.它们何时出现以及它们何时出现. 执行计划中的每一个运算符都会提供一些有关 SQL Server 运行方式的指标. 我们需要理解这 ...

  7. SQL Server实际执行计划COST欺骗案例

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

  8. [转]SQL Server 2000执行计划成本(1/5)

    表扫描 当没有合适的索引时就发生表扫描操作.这可能意味着没有索引存在或者预期有很多行且比扫描整个表开销更少.如果表是一个堆表,执行计划显示表扫描操作:如果表有聚集索引或者所有需要的值都在一个非聚集索引 ...

  9. sql子句的执行顺序_SQL Server查询执行计划– WHERE子句的示例

    sql子句的执行顺序 previous part of this article, we explained how indexes affect SQL Server query execution ...

最新文章

  1. oracle缩小表空间
  2. Acwing第 37 场周赛【完结】
  3. {网络编程}和{多线程}应用:基于UDP协议【实现多发送方发送数据到同一个接收者】--练习
  4. 全球与中国文件夹架市场研究与商业模式创新分析报告2022-2028年
  5. Docker Compose配置springboot微服务项目
  6. 关于遍历字典的二三事
  7. MYSQL学习笔记2--mysql 静态和动态plugin
  8. 升级IOS15.4出现更新验证失败如何解决
  9. Java导出word模板
  10. html在搜索栏中加入放大镜,CSS3 搜索条动画(放大镜图标展开为长方形输入框)...
  11. 程序员双手飞快敲键盘的时候是在敲代码吗?
  12. 干货总结!太全面了,图解SQL面试题:经典30题!
  13. DOS计算机设置登录密码,电脑开机的时候有个DOS的密码怎么设置啊、
  14. Golang字符串拼接
  15. 魔兽修改默认服务器,魔兽怎么设置默认服务器
  16. .sbt文件的配置详解
  17. Swing错鸡包 特殊历史脚本
  18. ##if语句与if语句的嵌套
  19. 【VSCode报错】 Error while fetching extensions : XHR failed
  20. 打pythonadb组合拳,实现微信读书永久免费读

热门文章

  1. android创建sqlite在sd卡上,在ionic3的sdcard上创建SQLite数据库
  2. 参加软件测试工程师面试前,这些内容你一定要准备
  3. PAT甲级1013 (图,DFS举例详解)
  4. mysql 表名规范_MYSQL数据库命名及设计规范
  5. 轧机用弹性阻尼体反力计算_「轴承知识」轧机轴承故障频繁怎么办?必要的检查维护不可少...
  6. define macro in xcode project
  7. 二维标准正态分布的matlab方程
  8. pytorch数据增强
  9. 输入一行字符,用Java分别统计出其中英文字母、空格、数字和其他字符的个数。
  10. AI学习笔记(四)相机模型、图像聚类算法