我们知道在SqlServer中,索引对查询语句的优化起着巨大的作用,一般来说在执行计划中出现了Index Seek的步骤,我们就认为索引命中了。但是Index Seek中有两个部分是值得我们注意的,我们来查看下面一个查询语句:

select [Title],[FirstName],[MiddleName],[LastName]
from [dbo].[DimCustomer]
where [MaritalStatus]=N'M' and [Gender]=N'F' and [NumberChildrenAtHome]>0 and [TotalChildren]=3

该语句从表[DimCustomer]查询列[Title],[FirstName],[MiddleName],[LastName], 查询条件用到了列[MaritalStatus],[Gender],[NumberChildrenAtHome],[TotalChildren]。我们知道建立索引的原则一般是把where条件中的列作为索引列(SqlServer对Where条件中列的使用方式也有要求,如果Where条件中的列使用不当,将导致索引不会被命中,但这不是本文讨论的重点),将在select后面的查询列作为Include列,所以根据这个原则我们建立如下非聚集索引[IX_MaritalStatus_Gender_TotalChildren_NumberChildrenAtHome]

CREATE NONCLUSTERED INDEX [IX_MaritalStatus_Gender_TotalChildren_NumberChildrenAtHome] ON [dbo].[DimCustomer]
(
[MaritalStatus] ASC,
[Gender] ASC,
[NumberChildrenAtHome] ASC,
[TotalChildren] ASC
)
INCLUDE ( [Title],
[FirstName],
[MiddleName],
[LastName])

然后我们运行最上面的查询语句得到如下执行计划:

我们可以看到该执行计划中,出现了Index Seek的步骤,并且正是我们创建的索引[IX_MaritalStatus_Gender_TotalChildren_NumberChildrenAtHome],在Index Seek有两个非常重要的部分,上图中用红线标出,一个是Seek Predicates,另一个是Predicate。Seek Predicates是索引中对性能提升最大的因素,因为SqlServer会使用Seek Predicates中出现的列直接去索引B树上做快速查找,也就是Seek Predicates所包含的列越多那么对整个查询性能的提升就越大,当SqlServer通过Seek Predicates中的列筛选出数据后,会用Predicate中的列在此基础上做二次筛选,所以Predicate实际上是在Seek Predicates筛选后的数据上再做筛选。

所以我们知道了在Index Seek中Seek Predicates中出现的列越多,那么对查询性能的提升就会越大,那么问题是如何让执行计划将尽可能多的列放入Seek Predicates中呢?答案就是在Sql语句的where条件中的列,必须是索引列中第一列的相邻顺序列。

那这是什么意思呢?拿本例建立的索引[IX_MaritalStatus_Gender_TotalChildren_NumberChildrenAtHome]来说,那么4个索引列按从左到右的顺序是

[MaritalStatus] , [Gender] , [NumberChildrenAtHome] , [TotalChildren]

  • 那么在Select查询的where条件中就必须出现[MaritalStatus] 列,因为[MaritalStatus]是索引的第一个索引列(索引中的第一索引列相当重要,它包含了索引的统计信息,详细信息可以查阅相关文档),所以它必须出现在Where条件中,这时Seek Predicates中就会包含列[MaritalStatus]了。
  • 接下来如果你想要Seek Predicates中包含更多的列那么[Gender]列必须也要出现在Where条件中([Gender]在Where条件中的顺序不重要,但是[MaritalStatus]和[Gender]必须都出现在Where条件中),因为在索引中从左到右相邻[MaritalStatus]的列就是[Gender]列,所以这时Seek Predicates就会包含[MaritalStatus]和[Gender]列。
  • 如果你想要Seek Predicates中再包含更多的列,那么就该轮到[NumberChildrenAtHome]出现在Where条件中了,原因是在索引中相邻[MaritalStatus]和[Gender]的列是[NumberChildrenAtHome],所以当[NumberChildrenAtHome]列也出现在Where条件之后,Seek Predicates就会包含[MaritalStatus],[Gender]和[NumberChildrenAtHome]列。
  • 如果你还想要Seek Predicates中包含更多的列,那么最后就该轮到 [TotalChildren]出现在Where条件中了,原因是在索引中相邻[MaritalStatus],[Gender],[NumberChildrenAtHome]的列只剩下了 [TotalChildren],所以当 [TotalChildren]也出现在Where条件之后,不出意外的话(后面会解释为什么会有意外情况),Seek Predicates就会包含[MaritalStatus],[Gender],[NumberChildrenAtHome]和 [TotalChildren]这四列。

那么执行本文最上面的查询语句看看,结果和我们上面说的是否一致。查看执行计划的Index Seek步骤,我们惊讶的发现尽管[MaritalStatus],[Gender],[NumberChildrenAtHome]列和我们上面说的一样出现在了Seek Predicates中,但是最后一个索引列[TotalChildren]却出现在了Predicate中,这是因为我们对[NumberChildrenAtHome]列使用了范围性条件([NumberChildrenAtHome]>0),所以SqlServer判定查找[NumberChildrenAtHome]列的值时,会查找多个关于[NumberChildrenAtHome]列的B树节点,所以在Seek Predicates中的列就只能到[NumberChildrenAtHome]了,最后的索引列[TotalChildren]只能放在Predicate中进行二次筛选。

所以我们在这里总结下,由于索引[IX_MaritalStatus_Gender_TotalChildren_NumberChildrenAtHome]的索引列的顺序依次是

[MaritalStatus] , [Gender] , [NumberChildrenAtHome] , [TotalChildren]

所以要让上面四列出现在Index Seek步骤的Seek Predicates中,必须让Sql语句的Where条件列是如下几种组合之一

[MaritalStatus]  此时出现在Seek Predicates中的列为[MaritalStatus]

[MaritalStatus] , [Gender]  此时出现在Seek Predicates中的列为[MaritalStatus] , [Gender]

[MaritalStatus] , [Gender] , [NumberChildrenAtHome]  此时出现在Seek Predicates中的列为[MaritalStatus] , [Gender] , [NumberChildrenAtHome]

[MaritalStatus] , [Gender] , [NumberChildrenAtHome] , [TotalChildren]  此时出现在Seek Predicates中的列为[MaritalStatus] , [Gender] , [NumberChildrenAtHome](由于[NumberChildrenAtHome]列使用了范围性条件,导致SqlServer会在B树中查找[NumberChildrenAtHome]列的多个节点,所以[NumberChildrenAtHome]之后的索引列[TotalChildren],最终被查询优化器判定为无法出现在Seek Predicates中,只能在Predicate中进行二次筛选) 

除此之外的任何情况都会导致Seek Predicates中不包含预期的索引列,例如如果我们只在Where中包含[MaritalStatus]列和 [NumberChildrenAtHome]列

select [Title],[FirstName],[MiddleName],[LastName]
from [dbo].[DimCustomer]
where [MaritalStatus]=N'M' and [NumberChildrenAtHome]>0

那么我们会发现Index Seek步骤的Seek Predicates中只包含了[MaritalStatus],因为按照[MaritalStatus] , [Gender] , [NumberChildrenAtHome] , [TotalChildren]从左到右的顺序只有列[MaritalStatus]能匹配上,而 [NumberChildrenAtHome]列前面的[Gender]没有出现在Where条件中,所以[NumberChildrenAtHome]被放到了Predicate中。

又如下面的语句,Where中只包含了[Gender]和[NumberChildrenAtHome]列

select [Title],[FirstName],[MiddleName],[LastName]
from [dbo].[DimCustomer]
where [Gender]=N'F' and [NumberChildrenAtHome]>0

最后执行计划中的Index Seek步骤中的Seek Predicates没有包含任何列,因为按照[MaritalStatus] , [Gender] , [NumberChildrenAtHome] , [TotalChildren]从左到右的顺序,在[Gender]和[NumberChildrenAtHome]列之前有[MaritalStatus]列,但是[MaritalStatus]列并没有出现在上面Sql语句的Where条件当中,所以[Gender]和[NumberChildrenAtHome]列都不符合Seek Predicates的匹配规则,最终都被放到了Predicate中。

SqlServer执行计划中Index Seek步骤的匹配规则非常复杂,索引会不会被命中(Index Seek)及Seek Predicates中是否是预期的列,还和Sql语句中Where条件的写法有关,例如Where条件中如果有索引列用到了Sql函数那么索引命中的概率就会大大降低。本文只是对本人当前了解到的情况做了一些归纳和总结,如果后面发现了更多的奥秘,我会接着写新的文章来分享。

SqlServer中Index Seek的匹配规则(一)的更多相关文章

  1. SqlSever中Index Seek的匹配规则(一)

    我们知道在SqlServer中,索引对查询语句的优化起着巨大的作用,一般来说在执行计划中出现了Index Seek的步骤,我们就认为索引命中了.但是Index Seek中有两个部分是值得我们注意的,我 ...

  2. Android中的Intent Filter匹配规则介绍

    本文主要介绍了隐式Intent匹配目标组件的规则,若有叙述不清晰或是不准确的地方希望大家指出,谢谢大家: ) 1. Intent简介 Intent用于在一个组件(Component,如Activity ...

  3. SqlServer nvarchar中的中文字符匹配,更改SqlServer实例和数据库排序规则的办法

    我们都知道在SqlServer中的nvarchar类型可以完美的存储诸如中文这种unicode字符,但是我们会发现有时候查询语句去查询nvarchar列的时候查不出来. 为什么nvarchar类型有时 ...

  4. Sql Server中的表访问方式Table Scan, Index Scan, Index Seek

    1.oracle中的表访问方式 在oracle中有表访问方式的说法,访问表中的数据主要通过三种方式进行访问: 全表扫描(full table scan),直接访问数据页,查找满足条件的数据 通过row ...

  5. 转:Sql Server中的表访问方式Table Scan, Index Scan, Index Seek

    0.参考文献 Table Scan, Index Scan, Index Seek SQL SERVER – Index Seek vs. Index Scan – Diffefence and Us ...

  6. web.xml中url-pattern匹配规则

    小知识 一般的URL组成 URL = 服务器地址 + RequestURI 例如URI:http://localhost:8080/practice/main [http://localhost:80 ...

  7. Go的http包中默认路由匹配规则

    # 一.执行流程 首先我们构建一个简单http server: ```go package main import ( "log" "net/http" ) f ...

  8. 在JaveWeb项目中配置Spring 匿名访问时,匹配规则的变相实现/*

    实现/* /** * 根据当前的URL返回该url的角色集合. * 1.如果当前的URL在匿名访问的URL集合当中时,在当前的角色中添加匿名访问的角色(SysRole.ROLE_CONFIG_ANON ...

  9. Web.xml中设置Servlet和Filter时的url-pattern匹配规则

    一.servlet容器对url的匹配过程: 当一个请求发送到servlet容器的时候,容器先会将请求的url减去当前应用上下文的路径作为servlet的映射url,比如我访问的是http://loca ...

随机推荐

  1. 使用 CircleCI 2.0 进行持续集成/持续部署

    使用 CircleCI 2.0 进行持续集成/持续部署 - 简书https://www.jianshu.com/p/36af6af74dfc Signup - CircleCIhttps://circ ...

  2. linux下如何制作initramfs镜像?

    1. 准备文件 加入已经准备好了所有文件在/home/initrd-base目录下 2. 在内核中指定/home/initramfs-base目录 General setup -> (/home ...

  3. 小D课堂 - 零基础入门SpringBoot2.X到实战_第7节 SpringBoot常用Starter介绍和整合模板引擎Freemaker、thymeleaf_28..SpringBoot Starter讲解

    笔记 1.SpringBoot Starter讲解     简介:介绍什么是SpringBoot Starter和主要作用 1.官网地址:https://docs.spring.io/spring-b ...

  4. centos7设置rsyslog日志服务集中服务器

    centos7设置rsyslog日志服务集中服务器 环境:centos6.9_x86_64,自带的rsyslog版本是7.4.7,很多配置都不支持,于是进行升级后配置 # 安装新版本的rsyslog程 ...

  5. SpringBoot启动嵌入式tomcat源码解读

    一.SpringBoot自动拉起Tomcat SpringBoot框架是当前比较流行的java后端开发框架,与maven结合大大简化了开发人员项目搭建的步骤,我们知道SpringBoot的启动类启动后 ...

  6. 004-行为型-07-备忘录模式(Memento)

    一.概述 又叫做快照模式(Snapshot Pattern)或Token模式 保存对象的内部状态,并在需要的时候(undo/rollback)恢复对象以前的状态. 意图:在不破坏封装性的前提下,捕获一 ...

  7. Qt编写气体安全管理系统22-报警联动

    一.前言 报警联动功能不是一个常规的功能,一般是给客户定制的功能,比如探测器采集到的数据报警了,不仅本地要播放报警声音,存储报警记录,发送报警短信和邮件,还需要触发警号面板灯进行报警,而警号面板指示灯 ...

  8. GWAS: 网页版的基因型填充(genotype imputation)

    在全基因组关联分析中,处理芯片数据时,必须走的一个流程就是基因型数据填充(imputation). 当然,如果你拿到的是全测序的数据,请忽略这一步. 下面直奔主题,怎么在网页版进行基因型填充. 1 进 ...

  9. SQL Server 2014 清除用户名和密码

    网上找来找去都是SQL Server 2008版本或者以前版本的... 后来:http://stackoverflow.com/questions/349668/removing-the-rememb ...

  10. Speech Recognition Java Code - HMM VQ MFCC ( Hidden markov model, Vector Quantization and Mel Filter Cepstral Coefficient)

    Hi everyone,I have shared speech recognition code inhttps://github.com/gtiwari333/speech-recognition ...