其实本来这个问题没有什么好说的,今天优化的时候遇到一个SQL语句,因为比较有意思,所以我截取、简化了SQL语句,演示给大家看,如下所示

declare @bamboo_Code varchar(3);

 

set @bamboo_Code='-01';

 

 

SELECT DISTINCT yarn_lot

FROM   dbo.rsjob WITH ( nolock )

WHERE  RIGHT(ges_no, 3) = @bamboo_Code

       AND Isnull(yarn_lot, '') <> ''; 

如上所示,SQL中对列yarn_log 使用了Isnull(yarn_lot, '') <> ''这种写法,我估计书写该SQL语句的人应该是深信了“is null 和 is not null 将会导致索引失效”这条网上流传的教条, 至于这个建议是从哪里流传开来,已经无法考证。 那么我们通过实践来验证一下is null 或 is not null 是否会导致索引失效。

表rsjob是一个堆表,在列yarn_lot上建有索引yarn_lot.那么我们通过实验来验证吧

SELECT DISTINCT yarn_lot

FROM   dbo.rsjob WITH(nolock)

WHERE  yarn_lot IS NOT NULL;

 

 

SELECT DISTINCT yarn_lot

FROM   dbo.rsjob WITH(nolock)

WHERE  yarn_lot IS NULL 

如上所示,不管是IS NULL 或IS NOT NULL都走了索引查找。

declare @bamboo_Code varchar(3);

 

set @bamboo_Code='-01';

 

 

SELECT DISTINCT yarn_lot

FROM   dbo.rsjob WITH ( nolock )

WHERE  RIGHT(ges_no, 3) = @bamboo_Code

       AND Isnull(yarn_lot, '') <> ''; 

       

       

SELECT DISTINCT yarn_lot

FROM   dbo.rsjob WITH ( nolock )

WHERE  RIGHT(ges_no, 3) = @bamboo_Code

       AND yarn_lot IS NOT NULL;

另外我们来看看这两个原始SQL执行计划的开销比值为52:48, 也就是说使用IS NOT NULL性能更好,第一个SQL语句由于做了转换,导致其走索引扫描,而使用IS NOT NULL则走索引查找。

“is null 和 is not null 将会导致索引失效”这种坑人教条直接被推翻了。所以还在信奉这个教条的人真应该自己动手验证一下。

下面我们可以通过实验验证一下,考虑到在真实环境中,可能情况比较复杂。我们可以构建下面几个场景。其实真实环境中情况还会复杂一些。但是基本上大致有如下一些场景

 

情况1:堆表 谓词上单独索引列

USE Test;

GO

DROP TABLE TEST;

GO

  

CREATE TABLE TEST (OBJECT_ID  INT, NAME VARCHAR(12));

 

CREATE INDEX PK_TEST ON TEST(OBJECT_ID) INCLUDE(NAME);

 

DECLARE @Index INT =0;

 

WHILE @Index < 10000

BEGIN

    INSERT INTO TEST

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

   

    SET @Index = @Index +1;

END

 

INSERT INTO TEST

SELECT NULL, 'only test1' UNION ALL

SELECT NULL, 'only test2'

 

UPDATE STATISTICS TEST WITH FULLSCAN;

 

SELECT * FROM TEST WHERE OBJECT_ID IS NULL;

SELECT * FROM TEST WHERE  OBJECT_ID IS NOT NULL;

删除索引,建立如下索引。如下所示

DROP INDEX PK_TEST ON TEST;

CREATE INDEX PK_TEST ON TEST(OBJECT_ID)

由此可见IS NULL 或IS NOT NULL的执行计划即与索引有关系,还跟数据分布有一定关系。

情况2:堆表 谓词上无索引

USE Test;

GO

DROP TABLE TEST;

GO

 

CREATE TABLE TEST (OBJECT_ID  INT, NAME VARCHAR(12));

 

 

 

DECLARE @Index INT =0;

 

WHILE @Index < 10000

BEGIN

    INSERT INTO TEST

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

   

    SET @Index = @Index +1;

END

 

INSERT INTO TEST


SELECT NULL, 'only test1' UNION ALL

SELECT NULL, 'only test2'

 

UPDATE STATISTICS TEST WITH FULLSCAN;

 

 

SELECT * FROM TEST WHERE OBJECT_ID IS NULL;

SELECT * FROM TEST WHERE  OBJECT_ID IS NOT NULL;

如上所示,如果一个堆表没有建立任何索引,那么使用IS NULL 或IS NOT NULL肯定要走全表扫描,不过这不在我们的讨论范围之内。然后我们看看将索引建立在其它字段上(主要是为了与聚集索引表对比),它依然全表扫描。

CREATE INDEX PK_TEST ON TEST(OBJECT_ID) INCLUDE(NAME);

 

INSERT INTO TEST

 SELECT 10000, NULL UNION ALL

 SELECT 10001, NULL ;

 

SELECT * FROM TEST WHERE NAME  IS NULL;

SELECT * FROM TEST WHERE  NAME IS NOT NULL;

 

情况3:堆表 联合索引列

USE Test;

GO

DROP TABLE TEST;

GO

   

CREATE TABLE TEST (OBJECT_ID  INT, NAME VARCHAR(12), AGE INT);

 

CREATE INDEX IDX_TEST_N1 ON TEST(NAME, AGE);

 

DECLARE @Index INT =0;

 

WHILE @Index < 10000

BEGIN

    INSERT INTO TEST

    SELECT @Index, 'kerry'+ CAST(@Index AS VARCHAR), floor(rand()*100) ;

   

    SET @Index = @Index +1;

END

 

 

INSERT INTO TEST

SELECT NULL, 'only test1', 12 UNION ALL

SELECT NULL, 'only test2',24

UPDATE STATISTICS TEST WITH FULLSCAN;

 

 

SELECT * FROM TEST WHERE NAME IS NULL;

SELECT * FROM TEST WHERE  NAME IS NOT NULL;

如果联合索引中,谓词位于联合索引的第二或更后位置,那么又是什么情况? 从下面我们可以看到,SQL走全表扫描了。

DROP INDEX IDX_TEST_N1 ON TEST;

 

CREATE INDEX IDX_TEST_N1 ON TEST( AGE,NAME);

 

UPDATE STATISTICS TEST WITH FULLSCAN;

4 聚集索引表  单独索引列

USE Test;

GO

DROP TABLE TEST;

GO

   

CREATE TABLE TEST (OBJECT_ID  INT, NAME VARCHAR(12));

 

CREATE CLUSTERED INDEX PK_TEST ON TEST(OBJECT_ID)

 

DECLARE @Index INT =0;

 

WHILE @Index < 10000

BEGIN

    INSERT INTO TEST

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

   

    SET @Index = @Index +1;

END

INSERT INTO TEST

SELECT NULL, 'only test1' UNION ALL

SELECT NULL, 'only test2'

SELECT * FROM TEST WHERE OBJECT_ID IS NULL;

SELECT * FROM TEST WHERE  OBJECT_ID IS NOT NULL;

如果我在列NAME上面使用IS NULL 或IS NOT NULL进行查询,你会发现执行计划从聚集索引查找变为了聚集索引扫描。

INSERT INTO TEST 

 

SELECT 10000, NULL UNION ALL 

 

SELECT 10001, NULL ; 

 

SELECT * FROM TEST WHERE NAME IS NULL; 

 

SELECT * FROM TEST WHERE NAME IS NOT NULL;

4 聚集索引表  联合索引列

USE Test;

GO

DROP TABLE TEST;

GO

   

CREATE TABLE TEST (OBJECT_ID  INT, NAME VARCHAR(12), AGE INT);

 

CREATE CLUSTERED INDEX PK_TEST ON TEST(OBJECT_ID)

 

DECLARE @Index INT =0;

 

WHILE @Index < 10000

BEGIN

    INSERT INTO TEST

    SELECT @Index, 'kerry'+ CAST(@Index AS VARCHAR), floor(rand()*100) ;

   

    SET @Index = @Index +1;

END

 

 

INSERT INTO TEST

SELECT 10001, 'NULL', 12 UNION ALL

SELECT 10002, 'NULL',24

 

CREATE INDEX IDX_TEST_N2 ON TEST(NAME,AGE);

UPDATE STATISTICS TEST WITH FULLSCAN;

如果联合索引中,谓词位于不位于第一列,那么IS NULL 或IS NOT NULL有会不会走索引呢?

DROP INDEX IDX_TEST_N2 ON TEST; 

 

CREATE INDEX IDX_TEST_N2 ON TEST(AGE,NAME); 

 

UPDATE STATISTICS TEST WITH FULLSCAN; 

如上所示,它从索引查找变成索引扫描了。

小结: 1:“is null 和 is not null 将会导致索引失效”这种教条完全是狗屎,SQL Server的索引是包含了null 值,而Oracle的索引是不包含null值的。不同数据库情况有所不同,不要生搬硬套。

2:如果谓词上面建立有索引的话,基本上都会走索引,至于是走索引查找还是索引扫描与索引类型有一定关系,也与字段位于联合索引中位置有关系。另外,数据分布倾斜得非常厉害也会导致其走全表扫描而不走索引,但是这并不是说IS NULL 和 IS NOT NULL导致索引失效。有一点非常重要,通过观察SQL语句而推断执行计划是很不现实的,需要综合考察SQL语句所涉及表的索引、数据分布、统计信息,才能综合判断,用通俗的话来说要结合具体场景。

SQL SERVER 中is null 和 is not null 将会导致索引失效吗?的更多相关文章

  1. SQL Server中可能为null的变量逻辑运算的时候要小心

    DECLARE @a int declare @b int IF(@a<>@b) print('@a<>@b') else print('@a=@b') ) print('b& ...

  2. sql server中NULL导入decimal字段时报错

    sql server中NULL导入decimal字段时报错 在导入CSV文件时,如果decimal字段为null值,导致文本文件入库时失败. 错误现象 构造例子 新建一张表,包含decimal字段. ...

  3. SQL Server中NULL的一个测试

    我们都知道SQL Server中NULL是一个很特殊的存在,因为NULL不会等于任何值,且NULL也不会不等于任何值.对于NULL我们只能使用IS或IS NOT关键字来进行比较. 我们先来看看下面一个 ...

  4. SQL Server中的高可用性(2)----文件与文件组

        在谈到SQL Server的高可用性之前,我们首先要谈一谈单实例的高可用性.在单实例的高可用性中,不可忽略的就是文件和文件组的高可用性.SQL Server允许在某些文件损坏或离线的情况下,允 ...

  5. 在SQL Server中为什么不建议使用Not In子查询

        在SQL Server中,子查询可以分为相关子查询和无关子查询,对于无关子查询来说,Not In子句比较常见,但Not In潜在会带来下面两种问题: 结果不准确 查询性能低下       下面 ...

  6. sql server中对xml进行操作

    一.前言 SQL Server 2005 引入了一种称为 XML 的本机数据类型.用户可以创建这样的表,它在关系列之外还有一个或多个 XML 类型的列:此外,还允许带有变量和参数.为了更好地支持 XM ...

  7. SQL Server中查询数据库及表的信息语句

    /* -- 本文件主要是汇总了 Microsoft SQL Server 中有关数据库与表的相关信息查询语句. -- 下面的查询语句中一般给出两种查询方法, -- A方法访问系统表,适应于SQL 20 ...

  8. 再谈SQL Server中日志的的作用

    简介     之前我已经写了一个关于SQL Server日志的简单系列文章.本篇文章会进一步挖掘日志背后的一些概念,原理以及作用.如果您没有看过我之前的文章,请参阅:     浅谈SQL Server ...

  9. SQL Server 中的事务与事务隔离级别以及如何理解脏读, 未提交读,不可重复读和幻读产生的过程和原因

    原本打算写有关 SSIS Package 中的事务控制过程的,但是发现很多基本的概念还是需要有 SQL Server 事务和事务的隔离级别做基础铺垫.所以花了点时间,把 SQL Server 数据库中 ...

随机推荐

  1. 用CSS3动画,让页面动起来

    以前就听说过有个库,叫animate.css,但是自己并没有在实际项目中使用过,这次正好要做个招聘页面,得以利用一下这个库,在经常会卡顿的UC浏览器中也能流畅执行. 扫描下面的二维码,可以看到在线的d ...

  2. “英雄之旅”见闻和小结----angular2系列(三)

    前言: 本系列在前面两篇文章,介绍了Zone.js和angular2的基础概念.而后对于ng2的学习,还是由官方的 Tour of Heroes 开始. 以下内容经过提炼和个人理解,当然也会有不正确的 ...

  3. lamp 环境搭建

    LAMP指的Linux(操作系统).ApacheHTTP 服务器,MySQL(数据库软件)和PHP语言 使用wampserver软件,搭建环境.如下图: 双击程序包,安装最后一步随便选择一个浏览器打开 ...

  4. 微信扫码支付+Asp.Net MVC

    这里的扫码支付指的是PC网站上面使用微信支付,也就是官方的模式二,网站是Asp.net MVC,整理如下.(demo在最下方) 一.准备工作 使用的微信API中的统一下单方法,关键的参数是‘公众账号I ...

  5. gnuplot: 一种更为简洁的曲线,柱状图绘图软件

    gnuplot: 一种更为简洁的曲线,柱状图绘图软件 gnuplot: 一种更为简洁的曲线,柱状图绘图软件 Zhong Xiewei Wed Jun 25 gnuplot简单介绍 关于gnuplot的 ...

  6. MySQL PXC构建一个新节点只需IST传输的方法

    需求场景:原有的pxc环境数据量已经比较大,新买的服务器要加入此集群中,如何让其用IST的方式传输,而不是SST. PXC传输数据有两种方式: IST: Incremental State Trans ...

  7. Vue.js说说组件

    什么是组件:组件是Vue.js最强大的功能之一.组件可以扩展HTML元素,封装可重用的代码.在较高层面上,组件是自定义的元素,Vue.js的编译器为它添加特殊功能.在有些情况下,组件也可以是原生HTM ...

  8. Effective java笔记(三),类与接口

    类与接口是Java语言的核心,设计出更加有用.健壮和灵活的类与接口很重要. 13.使类和成员的可访问性最小化 设计良好的模块会隐藏起所有的实现细节,仅使用API与其他模块进行通信.这个概念称为信息隐藏 ...

  9. 关系数据库SQL之可编程性存储过程

    前言 前面关系数据库SQL之可编程性函数(用户自定义函数)一文提到关系型数据库提供了可编程性的函数.存储过程.事务.触发器及游标,前文已介绍了函数,本文来介绍一下存储过程的创建.执行.删除.(还是以前 ...

  10. IIS8 添加配置 WCF服务

    今天在Windows8.1 操作系统部署了半天的WCF 一直老是在报错.在这里做个记录 防止下次忘记 在网上查了半天.终于知道原来IIS8不支持WCF服务SVC的请求.所以必须要给IIS8添加WCF服 ...