原文:SQL Server优化技巧——如何避免查询条件OR引起的性能问题

之前写过一篇博客“SQL SERVER中关于OR会导致索引扫描或全表扫描的浅析”,里面介绍了OR可能会引起全表扫描或索引扫描的各种案例,以及如何优化查询条件中含有OR的SQL语句的几种方法,其实还有一些方法可以用来优化这种问题,这里简单介绍一下。

如下所示,下面的SQL语句之所有出现这种写法,是因为程序的查询界面,可能有多个输入性的查询条件,往往用户只填了一个或部分查询条件(业务情况,应该不用详细介绍,大家都能明白),但是程序里面没有通过判断查询条件生成不同的SQL语句,而是用一个SQL搞定,不管用户没有填写JobNo这个查询条件,下面这种写法:WHERE ISNULL(@JobNo, '') = ''  OR JobNo = @JobNo都能满足条件,实现逻辑功能。

DECLARE @GenerateDateStart DATETIME ,

    @GenerateDateEnd DATETIME ,

    @JobNo NVARCHAR(200) ,

    @GkNo NVARCHAR(200);

SET @JobNo = 'PT19B030';

SET @GkNo = 'PV19-1-8050'; 

 

  SELECT    *

  FROM      [dbo].[GEW_UnitConsumption] AS A

            LEFT JOIN dbo.UnitConsumption_Relation AS B ON B.UsableFlag = 'Y'

                                                           AND A.GewUnitConsumptionId = B.RootUnitConsumptionID

  WHERE     ( ISNULL(@JobNo, '') = ''

              OR A.JobNo = @JobNo

            )

            AND ( ISNULL(@GkNo, '') = ''

                  OR A.GkNo = @GkNo

                );

其实,如果根据查询条件动态生成SQL语句,的确能避免查询条件中出现OR的情形,但是动态SQL语句没有上面语句简单和通熟易懂,尤其是查询条件较多的情况下。只能说各有利弊。这里暂且不讨论那种策略的优劣。

下面介绍一种技巧,如何避免OR引起的索引扫描或全表扫描问题。我们可以使用CASE WHEN改写一下这个SQL语句,就能避免OR引起的执行计划不走索引查找(Index Seek)的情况,如下所示:

DECLARE @GenerateDateStart DATETIME ,

    @GenerateDateEnd DATETIME ,

    @JobNo NVARCHAR(200) ,

    @GkNo NVARCHAR(200);

SET @JobNo = 'PT19B030';

SET @GkNo = 'PV19-1-8050'; 

 

 

SELECT  *

FROM    [dbo].[GEW_UnitConsumption] AS A

        LEFT JOIN dbo.UnitConsumption_Relation AS B ON B.UsableFlag = 'Y'

                                                       AND A.GewUnitConsumptionId = B.RootUnitConsumptionID

WHERE   CASE WHEN ISNULL(@JobNo, '') = '' THEN A.JobNo

             ELSE @JobNo

        END = JobNo

        AND CASE WHEN ISNULL(@GkNo, '') = '' THEN A.GkNo

                 ELSE GkNo

            END = @GkNo;

测试对比发现性能改善非常明显,当然这种优化技巧也是有局限性的,并不能解决所有OR引起的性能问题(没有银弹!)。如下所示,对于下面这种情况,这种技巧也是无能为力!

SELECT * FROM TEST1 WHERE A=12 OR B=500

------------------------------------------分割线-------------------------------------------------

网友MSSQL123反馈:他测试的一个案例发现这种技巧无效,个人测试验证发现确实如此,后面发现个人遇到的仅仅是一个特殊个例(当时生产环境那个场景下确实生效了),后面经过大量测试发现,很多情况下CASE WHEN这种技巧无效,也就是说单个案例不具有通用性,后面进一步测试分析,发现我得出的结论是错误的

当然在错误的基础上,进一步测试验证,发现还是有技巧优化OR引起的性能问题的,这也是我后续补充的原因,请见下文分析:

我们首先简单构造一个测试环境案例,测试环境为SQL Server 2014

CREATE TABLE TEST_OPTION_COMPILE (OBJECT_ID  INT, NAME VARCHAR(16));

 

CREATE CLUSTERED INDEX PK_TEST_OPTION_COMPILE ON TEST_OPTION_COMPILE(OBJECT_ID); 

 

 

DECLARE @Index INT =0;

 

WHILE @Index < 100000

BEGIN

 

    INSERT INTO TEST_OPTION_COMPILE

    SELECT @Index, 'kerry'+CAST(@Index AS VARCHAR(7));

    

    SET @Index = @Index +1;

END

 

 

CREATE INDEX IX_TEST_OPTION_COMPILE_N1 ON TEST_OPTION_COMPILE(NAME);

UPDATE STATISTICS TEST_OPTION_COMPILE WITH FULLSCAN;

如下测试所示,发现这个例子中,CASE WHEN完全无效,使用这种SQL写法,依然走Index Scan

DECLARE @name VARCHAR(8);

SET @name = 'kerry8'

 

SELECT  NAME

FROM    dbo.TEST_OPTION_COMPILE

WHERE   CASE WHEN ISNULL(@name, '') = '' THEN NAME

             ELSE @name

        END = NAME;

 

SELECT  NAME

FROM    dbo.TEST_OPTION_COMPILE

WHERE   ( ISNULL(@name, '') = ''

          OR NAME = @name

        )

如果我们在SQL后面加上OPTION(RECOMPILE)的话,那么SQL就会走索引查找(Index Seek),其实下面两个SQL语句,如果都加上OPTION(RECOMPILE)的话,它们都会走索引。这是什么情况呢?

接下来我们对比分析一下,看看SQL语句有无OPTION(RECOMPILE)的区别,如下所示:

 

DECLARE @name VARCHAR(8);

SET @name = 'kerry8'

 

SELECT  NAME

FROM    dbo.TEST_OPTION_COMPILE

WHERE   CASE WHEN ISNULL(@name, '') = '' THEN NAME

             ELSE @name

        END = NAME ;

 

SELECT  NAME

FROM    dbo.TEST_OPTION_COMPILE

WHERE   CASE WHEN ISNULL(@name, '') = '' THEN NAME

             ELSE @name

        END = NAME OPTION(RECOMPILE)

如下所示,如果没有OPTION(RECOMPILE)的话,执行计划走Index Scan,预估行数(Estimated Number of Rows)是100000, 而实际行数(Actual Number of Rows)是1,

如果SQL中有OPTION(RECOMPILE)的话,执行计划走Index Seek,预估行数(Estimated Number of Rows)是1, 而实际行数(Actual Number of Rows)是1,从对比我们可以看出,加上OPTION(RECOMPILE)的话,SQL的执行计划要准确很多,那么为什么呢?这里是因为OPTION(RECOMPILE)开启了Parameter Embedding Optimization

关于Parameter Embedding Optimization,这里简单介绍一下,详情参考Parameter Sniffing, Embedding, and the RECOMPILE Options参考资料的相关文档。

参数嗅探值使优化器可以使用参数值来得出基数估计。 WITH RECOMPILE和OPTION(RECOMPILE)均会生成查询计划,并根据每次执行时的实际参数值计算出估算值。

  相比WITH RECOMPILE这种强制重编译的方式,OPTION(RECOMPILE)中的参数嵌入优化(Parameter Embedding Optimization)的机制更进一步:查询解析期间,查询参数被文字常量值替代。 解析器能够神奇的将复杂问题简单化,并且在随后的查询优化可能会进一步完善这些内容。

Microsoft在SQL Server 2008(后RTM)中引入了参数嵌入优化(Parameter Embedding Optimization)。 这个特性扩展了参数嗅探优化。 它能使用基数估计值来嗅探参数以影响计划。具体参考官方文档“Changed behaviour of OPTION RECOMPILE syntax in SQL Server 2008 SP1 cumulative update #5

总结: 我们可以使用OPTION(RECOMPILE)(确切的说,是Parameter Embedding Optimization)这种技巧来避免查询条件中OR引起的性能问题,这确实是一个SQL Server优化技巧,至于我前面的结论,这是一个错误结论(使用CASE WHEN改写一下这个SQL语句,就能避免OR引起的执行计划不走索引查找(Index Seek))。在缺乏严谨的论证、充分的测试就草率的得出了一个结论,以后要引以为戒!。

参考资料:

https://www.cnblogs.com/wy123/p/6262800.html

https://sqlperformance.com/2013/08/t-sql-queries/parameter-sniffing-embedding-and-the-recompile-options

SQL Server优化技巧——如何避免查询条件OR引起的性能问题的更多相关文章

  1. SQL Server优化技巧——如何避免查询条件OR引起的性能问题

    之前写过一篇博客"SQL SERVER中关于OR会导致索引扫描或全表扫描的浅析",里面介绍了OR可能会引起全表扫描或索引扫描的各种案例,以及如何优化查询条件中含有OR的SQL语句的 ...

  2. SQL Server优化技巧之SQL Server中的"MapReduce"

    日常的OLTP环境中,有时会涉及到一些统计方面的SQL语句,这些语句可能消耗巨大,进而影响整体运行环境,这里我为大家介绍如何利用SQL Server中的”类MapReduce”方式,在特定的统计情形中 ...

  3. Sql Server 优化技巧

    1.查看执行时间和cpu占用时间 set statistics time on select * from dbo.Product set statistics time off 打开你查询之后的消息 ...

  4. SQL Server优化的方法

    SQL Server优化的方法<一>   查询速度慢的原因很多,常见如下几种:   1.没有索引或者没有用到索引(这是查询慢最常见的问题,是程序设计的缺陷)   2.I/O吞吐量小,形成了 ...

  5. 深入SQL Server优化【推荐】

    深入sql server优化,MSSQL优化,T-SQL优化,查询优化 十步优化SQL Server 中的数据访问故事开篇:你和你的团队经过不懈努力,终于使网站成功上线,刚开始时,注册用户较少,网站性 ...

  6. SQL Server优化器特性-隐式谓词

    我们都知道,一条SQL语句提交给优化器会产生相应的执行计划然后执行输出结果,但他的执行计划是如何产生的呢?这可能是关系型数据库最复杂的部分了.这里我为大家介绍一个有关SQL Server优化器的特性- ...

  7. Sql Server优化之路

    本文只限coder级别层次上对Sql Server的优化处理简结,为防止专业DB人士有恶心.反胃等现象,请提前关闭此页面. 首先得有一个测试库,使用数据生成计划生成测试数据库(参考:http://de ...

  8. 【SQL Server 优化性能的几个方面】(转)

    转自:http://blog.csdn.net/feixianxxx/article/details/5524819     SQL Server 优化性能的几个方面 (一).数据库的设计 可以参看最 ...

  9. SQL Server 优化器特性导致的内存授予相关BUG

    我们有时会遇到一些坑,要不填平,要不绕过.这里为大家介绍一个相关SQL Server优化器方面的特性导致内存授予的相关BUG,及相关解决方式,也顺便回答下邹建同学的相关疑问. 问题描述 一个简单的查询 ...

随机推荐

  1. js产生一个随机的字符串数字组合

    /** * 随机生成字符串 * @param randomFlag 产生任意长度随机字母数字组合 * @param min 任意长度最小位[固定位数] * @param max 任意长度最大位 * @ ...

  2. vue中用watch监听当前路由

    之前做项目时,特别是后台项目,左边都有侧边栏,我们需要做到点击某个侧边栏的项让这个项高亮,之前采用的是给每个项绑定一个值,点击某个项时,就将这个值付给一个变量,在每一项上判断这个变量是否与每项上的值相 ...

  3. Linux+CLion+树莓派远程编译时,Cmake编译出现undefined reference to 'dlopen'的解决办法

    在Clion中链接讯飞的语音库并传至树莓派上编译时,出现如下错误. undefined reference to `dlopen' undefined reference to `dlclose' u ...

  4. [mysql]my.cnf在哪里

    linux mysql --help|grep my.cnf 查找顺序:/etc/my.cnf /etc/mysql/my.cnf /usr/local/mysql/etc/my.cnf ~/.my. ...

  5. Java中jdk代理和cglib代理

    代理模式 给某一个对象提供一个代理,并由代理对象控制对原对象的引用.在一些情况下,一个客户不想或者不能够直接引用一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用. 在Java中代理模式从实 ...

  6. POJ 3177 (Redundant Paths) —— (有重边,边双联通,无向图缩点)

    做到这里以后,总算是觉得tarjan算法已经有点入门了. 这题的题意是,给出若干个点和若干条边连接他们,在这个无向图中,问至少增加多少条边可以使得这个图变成边双联通图(即任意两点间都有至少两条没有重复 ...

  7. godaddy SSL证书不信任

    在使用网上教程的部署godaddy证书,会出现证书不受信任的情况. 各别审核比较严格的浏览器会阻止或者要求添加例外.情况如下: 利用在线证书测试工具会提示根证书的内容为空.从而导致证书不受信任. 解决 ...

  8. 【Eureka】Eureka 是什么

    Eureka是什么? Eureka 是 Netflix 的一个子模块,也是核心模块之一.Eureka 是一个基于 Rest 的服务,用于定位服务,以实现云端中间层服务发现和故障转移.服务注册和发现对于 ...

  9. 处理flutter http请求添加application/json报错Cannot set the body fields of a Request with content-type “application/json”

    在flutter中在http请求发送时设置"content-type": "application/json"会出现报错Cannot set the body ...

  10. docker save/load以及export/import使用测试

    对于有些环境需要离线安装的情况,docker以及docker容器都需要能够支持离线安装,对于docker离线安装,比较简单,按照https://www.cnblogs.com/qq931399960/ ...