SQL SERVER 2014 下IF EXITS 居然引起执行计划变更的案例分享

这个问题是在SQL SERVER 2005 升级到SQL SERVER 2014的测试过程中一同事发现的。我觉得有点意思,遂稍微修改一下脚本展示出来,本来想构造这样的一个案例来演示,但是畏惧麻烦,遂直接贴上原表,希望Leader不要叼我(当然个人觉得真没啥,两张表名而已,真泄露不了啥信息)。

脚本如下所示,非常简单的一段SQL语句,我将其分为SQL1、SQL2、SQL3.  其实SQL2、SQL3是差不多的,唯一的区别在于多了一个IF EXISTS

DECLARE @Operation_Code CHAR(3) ,
    @FNCardList VARCHAR(1000) ,

    @RollList VARCHAR(1000) ,

    @White VARCHAR(20) ,

    @OneMinute VARCHAR(20) ,

    @Operator VARCHAR(20) ,

    @Is_NoWait BIT ,

    @HoldCards VARCHAR(3000);            

 

 

SELECT  @Operation_Code = ‘999‘ ,

        @FNCardList = ‘A15309913‘ ,

        @RollList = ‘A15309913‘;

 

 

--SQL 1

DECLARE @FNCardTable TABLE ( Iden INT, FN_Card CHAR(9) ); 

 

 

INSERT  INTO @FNCardTable

        SELECT  Iden ,

                [No]

        FROM    PUBDB.dbo.udf_ConvertStrToTable(@FNCardList, ‘,‘) a;            

 

 

--SQL 2          

SELECT  1

FROM    dbo.fnRepairOperation a WITH ( NOLOCK )

        INNER JOIN @FNCardTable b ON CHARINDEX(b.FN_Card, a.FN_Card) > 0

        INNER JOIN dbo.fnJobTraceHdr c WITH ( NOLOCK ) ON c.FN_Card = b.FN_Card

                                                          AND c.Current_Department = a.Current_Department

WHERE   a.Check_Time IS NULL

        AND a.Is_Ignore = 0;

 

PRINT ( @Operation_Code );     

 

 

--SQL 3      

 

IF EXISTS ( SELECT   1

            FROM    dbo.fnRepairOperation a WITH ( NOLOCK )

                    INNER JOIN @FNCardTable b ON CHARINDEX(b.FN_Card,

                                                        a.FN_Card) > 0

                    INNER JOIN dbo.fnJobTraceHdr c WITH ( NOLOCK ) ON c.FN_Card = b.FN_Card

                                                        AND c.Current_Department = a.Current_Department

            WHERE   a.Check_Time IS NULL

                    AND a.Is_Ignore = 0 )

    BEGIN            

        RAISERROR(‘返回错误!‘, 16, 1);            

        RETURN;            

    END;  

在SQL SERVER 2005的环境中,整个批处理的SQL执行只需要不到1秒的样子。我们也能看到执行计划的COST对比值为0%,99%,1%。

在SQL SERVER 2014(SQL Server 2014 - 12.0.2000.8 Standard Edition )中执行时间突然变成了4分41秒。 最奇怪的是查询计划的COST比值依然为0%,99%,1%。实际测试发现这个COST的比值是不准确的。因为单独执行SQL1、SQL2只需要一秒。但是执行SQL3就需要4分多钟。(当然SQL SERVER 2005 与SQL SERVER 2014的数据,索引是一致的,细心的人会注意下面提示缺少索引,加上这个索引依然慢的出奇,这个影响因素完全可以忽略)

SQL 2的实际执行计划如下所示

SQL 3的实际执行计划如下所示

另外,表dbo.fnRepairOperation的记录数有332553,dbo.fnJobTraceHdr 的记录数为110058。表变量@FNCardTable记录数为1.对比执行计划,我们可以看到两者的Nested Loops的外部表变化了,从表变量@FNCardTable变成了dbo.fnRepairOperation

我们先来看看SQL2执行计划里面的一些详细信息,我们可以看到外边循环表为@FNCardTable,循环次数为1(Actual Number of Rows 值为1),内部循环表为dbo.fnJobTraceHdr,循环次数为1(Number of Executions为1),符合条件的记录集数据为1条(Actual Number of Rows 值为1)

那么再来看SQL3, 外部循环表变为dbo.fnRepairOperation,它走表扫描(Table Scan),循环次数为432(Actual Number of Rows),内部循环表为dbo.fnJobTraceHdr, 走索引扫描,总共循环了47545056次,这个值怎么来的呢? 因为内部循环表中符合记录数为110058(表dbo.fnJobTraceHdr的记录数), 110058*432 = 47545056,也就是说总共循环了四千七百多万次。 偶的神啊。难怪如此之慢。起初,我以为是统计信息不准确导致数据库优化器选择了错误的执行计划,于是我更新了这两个表的统计信息,甚至连索引也重建了。结果还是如此。看来的确是优化器没有选择最优的执行计划。但是没有IF EXITS它又是正常的, 加了IF EXITS后执行计划就变成这个鸟样。说不清是优化器的bug还是算法问题所导致。

那么怎么解决这个问题,可以用联接提示(HASH JOIN HINT)指定SQL语句走HASH JOIN,此时批处理的SQL语句可以1秒出来。另外就是改写该SQL语句的写法。在此不做过多阐述

IF EXISTS ( SELECT   1
            FROM    dbo.fnRepairOperation a WITH ( NOLOCK )

                    INNER JOIN @FNCardTable b ON CHARINDEX(b.FN_Card,

                                                        a.FN_Card) > 0

                    INNER HASH JOIN  dbo.fnJobTraceHdr c WITH ( NOLOCK ) ON c.FN_Card = b.FN_Card

                                                        AND c.Current_Department = a.Current_Department

            WHERE   a.Check_Time IS NULL

                    AND a.Is_Ignore = 0 )

    BEGIN            

        RAISERROR(‘部分卡中有 班长新增加的工序或 回修工序,请联系一下工艺员和当班班长!‘, 16, 1);            

        RETURN;            

    END; 

其实这个案例也间接验证了嵌套循环连接,随着数据量的增长,这种方式对性能的消耗将呈现出指数级别的增长。

时间: 2024-10-11 03:34:49

SQL SERVER 2014 下IF EXITS 居然引起执行计划变更的案例分享的相关文章

第16/24周 SQL Server 2014中的基数计算

大家好,欢迎回到性能调优培训.上个星期我们讨论在SQL Server里基数计算过程里的一些问题.今天我们继续详细谈下,SQL Server 2014里引入的新基数计算. 新基数计算 SQL Server 2014里一个增强是新的基数计算.上个星期你已经学到老基数计算有些限制,会生成错误的估计,这会导致不好的执行计划表现.截至SQL Server 2012,你一直在使用自SQL Server 7.0引入的基数计算. 当然,几年来也有很多问题被修正,但默认它们都没启用的——你需要启用SQL Serv

SQL Server 2014的数据库引擎新增功能(参考sqlserver官方文档)

SQL Server 2014数据库引擎引入了一些新功能和增强功能,这些功能可以提高设计.开发和维护数据存储系统的架构师.开发人员和管理员的能力和工作效率. 以下是数据库引擎已增强的方面. 数据库引擎功能增强 内存优化表 内存中 OLTP 是一种内存优化的数据库引擎,它集成到 SQL Server 引擎中. 内存中 OLTP 已针对 OLTP 进行优化. SQL Server Azure 中的数据文件 Azure 中的 SQL Server 数据文件可为作为SQL Server Azure bl

SQL Server 2014 Database Mail重复发送邮件特殊案例

在一数据库服务器(Microsoft SQL Server 2014 (SP2) (KB3171021) - 12.0.5000.0 (X64))发现有个作业调用Database Mail发送邮件时,有时候出现同样的邮件发送两封的情况,经过详细检查,排除了该作业里面业务逻辑有问题的情况,确实存在重复发送邮件的情况, 检查Database Mail日志,发现在0:00~0.03报"The mail could not be sent to the recipients because of the

SQL Server 2014 日志传送部署(1):日志传送概述

13.1 日志传送概述 13.1.1日志传送工作原理 日志传送是一种事务日志备份传送技术.日志传送允许从一个数据库(即主服务器上的主数据库)向多个在另外的服务器(即辅助服务器)上的数据库(即辅助数据库)自动发送事务日志备份.在辅助服务器上,这些事务日志备份被恢复到辅助数据库中,并和主数据库保持同步.一个可选的三级服务器(即监视服务器),记录事务日志备份.复制和恢复操作的历史和状态,以及这些操作依照计划不能发生时报警. 在日志传送中可配置一个主服务器实例向多台辅助服务器实例传送事务日志,在日志传送

SQL Server 2014 日志传送部署(2):日志传送系统要求和实验架构

13.2 部署日志传送 13.2.1 部署日志传送的系统要求 SQL Server 2014日志传送的部署对硬件基础设施有一定的要求,下面将概述这些系统要求. 网络 日志传送不一定需要Windows域环境,但是Windows域环境方便了日志传送的配置和管理:相对非域环境,其安全性提升不少. 参与日志传送的SQL Server服务器必须在网络中相互连通,主服务器能够将事务日志备份到共享文件夹,辅助服务器可以将事务日志备份复制到本地文件夹:监视服务器能够连接到主服务器和辅助服务器. 服务器和存储 主

SQL Server 2014新特性——Buffer Pool扩展

Buffer Pool扩展 Buffer Pool扩展是buffer pool 和非易失的SSD硬盘做连接.以SSD硬盘的特点来提高随机读性能. 缓冲池扩展优点 SQL Server读以随机读为主,SQL Server IO分为2部分:buffer pool管理方式,和buffer pool. SQL Server 从磁盘中读入数据,并且存放在buffer pool中以供读取和修改,修改完之后脏数据还是放在buffer pool中,当内存紧张执行lazy write把脏数据写入磁盘,并且释放内存

SQL Server 2014 高可用之一:Always On

关于Always On的概述,这里就不多说,需要的可以查看微软的官方网站:https://technet.microsoft.com/zh-cn/library/ff877884(v=sql.110).aspx 下面先介绍自己环境中的使用到的机器吧 计算机名 IP地址 备注 Contoso-SQL-A01 172.168.1.43 Always On节点1 Contoso-SQL-A02 172.168.1.44 Always On节点2 SQLAlwaysOn 172.168.1.45 群集地

thinkphp 3.2.3 连接sql server 2014 WAMPSERVER环境包

安装 sqlsrv 扩展 首先  sql server 2014 安装没啥说的 链接信息自己设置 php 版本 :5.5.12 sqlsrv 驱动  微软提供了 3.0 和3.1 版本  3.0 对应php 5.4  3.1对应着5.5 PS: win7 64位系统 下载sql server 驱动http://msdn.microsoft.com/en-us/sqlserver/ff657782.aspx Version 3.0 supports PHP 5.4 Version 3.1 supp

解读SQL Server 2014可更新列存储索引——存储机制

概述 SQL Server 2014被号称是微软数据库的一个革命性版本,其性能的提升的幅度是有史以来之最. 可更新的列存储索引作为SQL Server 2014的一个关键功能之一,在提升数据库的查询性能方面贡献非常突出.据微软统计,在面向OLAP查询统计类系统中,相比其他SQL传统版本的数据库,报表查询的性能最大可提升上十倍. 下面我们从存储的角度来了解下SQL Server 2014的可更新列存储索引. 什么是列存储 微软为了提升SQL Server的查询性能,更好的支持大数据分析,早在SQL