设为首页 收藏本站
查看: 770|回复: 0

[经验分享] SQL Server [索引] -- SQL SERVER 中is null 和 is not null 将会导致索引失效吗?

[复制链接]

尚未签到

发表于 2018-10-15 06:51:44 | 显示全部楼层 |阅读模式
  SQL SERVER 中is null 和 is not null 将会导致索引失效吗?
  其实本来这个问题没有什么好说的,今天优化的时候遇到一个SQL语句,因为比较有意思,所以我截取、简化了SQL语句,演示给大家看,如下所示
declare @bamboo_Code varchar(3);set @bamboo_Code='-01';SELECT DISTINCT yarn_lotFROM   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_lotFROM   dbo.rsjob WITH(nolock)WHERE  yarn_lot IS NOT NULL;SELECT DISTINCT yarn_lotFROM   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_lotFROM   dbo.rsjob WITH ( nolock )WHERE  RIGHT(ges_no, 3) = @bamboo_Code       AND Isnull(yarn_lot, '')  '';SELECT DISTINCT yarn_lotFROM   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;GODROP TABLE TEST;GOCREATE TABLE TEST (OBJECT_ID  INT, NAME VARCHAR(12));CREATE INDEX PK_TEST ON TEST(OBJECT_ID) INCLUDE(NAME);DECLARE @Index INT =0;WHILE @Index < 10000BEGIN    INSERT INTO TEST    SELECT @Index, 'kerry'+ CAST(@Index AS VARCHAR);    SET @Index = @Index +1;ENDINSERT INTO TESTSELECT NULL, 'only test1' UNION ALLSELECT 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;GODROP TABLE TEST;GOCREATE TABLE TEST (OBJECT_ID  INT, NAME VARCHAR(12));DECLARE @Index INT =0;WHILE @Index < 10000BEGIN    INSERT INTO TEST    SELECT @Index, 'kerry'+ CAST(@Index AS VARCHAR);    SET @Index = @Index +1;ENDINSERT INTO TESTSELECT NULL, 'only test1' UNION ALLSELECT 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;GODROP TABLE TEST;GOCREATE 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 < 10000BEGIN    INSERT INTO TEST    SELECT @Index, 'kerry'+ CAST(@Index AS VARCHAR), floor(rand()*100) ;    SET @Index = @Index +1;ENDINSERT INTO TESTSELECT NULL, 'only test1', 12 UNION ALLSELECT NULL, 'only test2',24UPDATE 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.1 聚集索引表  单独索引列
USE Test;GODROP TABLE TEST;GOCREATE TABLE TEST (OBJECT_ID  INT, NAME VARCHAR(12));CREATE CLUSTERED INDEX PK_TEST ON TEST(OBJECT_ID)DECLARE @Index INT =0;WHILE @Index < 10000BEGIN    INSERT INTO TEST    SELECT @Index, 'kerry'+ CAST(@Index AS VARCHAR);    SET @Index = @Index +1;ENDINSERT INTO TESTSELECT NULL, 'only test1' UNION ALLSELECT 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 TESTSELECT 10000, NULL UNION ALLSELECT 10001, NULL ;SELECT * FROM TEST WHERE NAME IS NULL;SELECT * FROM TEST WHERE NAME IS NOT NULL;
  情况4.2 聚集索引表  联合索引列
USE Test;GODROP TABLE TEST;GOCREATE 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 < 10000BEGIN    INSERT INTO TEST    SELECT @Index, 'kerry'+ CAST(@Index AS VARCHAR), floor(rand()*100) ;    SET @Index = @Index +1;ENDINSERT INTO TESTSELECT 10001, 'NULL', 12 UNION ALLSELECT 10002, 'NULL',24CREATE 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语句所涉及表的索引、数据分布、统计信息,才能综合判断,用通俗的话来说要结合具体场景。



运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-621636-1-1.html 上篇帖子: MS SQL Server和MySQL区别 下篇帖子: 安装与配置-Microsoft SQL Server 2000 Standard Edition服务器组件在此操作系统上
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表