SqlServer中Index Seek的匹配规则(一)

摘要:
我们知道,在SqlServer中,索引在查询语句的优化中发挥着巨大的作用。一般来说,当执行计划中出现IndexSeek步骤时,我们认为索引命中。答案是,Sql语句的where条件中的列必须是索引列中第一列的相邻序列。以本例中构建的索引[IX_MarialStatus_Gener_TotalChildren_NumberChildrenAtHome]为例。四个索引列从左到右依次为[MarkalStatus]、[Egender]、[NumberChildrenAtHome]和[TotalChildren]。然后,[MarkalStatus]列必须出现在Select查询的where条件中。因为[MarkStatus]是索引的第一个索引列,所以它必须出现在where条件中。此时,SeekPredicates将包含[MaritalStatus]列。

我们知道在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])


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

SqlServer中Index Seek的匹配规则(一)第1张

我们可以看到该执行计划中,出现了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中进行二次筛选。

SqlServer中Index Seek的匹配规则(一)第2张

所以我们在这里总结下,由于索引[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中。
SqlServer中Index Seek的匹配规则(一)第3张

又如下面的语句,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的匹配规则(一)第4张

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

免责声明:文章转载自《SqlServer中Index Seek的匹配规则(一)》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇vue,react项目中使用webpack打包中直接打包成压缩包的方法CAS服务器集群和客户端集群环境下的单点登录和单点注销解决方案下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

(转)SqlServer为大数据量表建索引

本文转载自:http://blog.csdn.net/iangujun/article/details/8136764 之前从没有用SqlServer数据库处理过大数据量的表,都是用Oracle,然后一般为数据量较大的表添加索引或主键都是用plsql工具,今天正好需要为一张保存于SqlServer数据库的千万级数据表增加索引,于是遇到了下面一系列的问题。...

SQLServer数据库(二)

数据库设计:就是将数据库中的数据库实体及这些数据库实体之间的关系,进行规划和结构化的过程。 项目开发过程: 需求分析 概要设计 详细设计 代码编写 运行测试 打包发行 数据库的系统分析基本步骤:收集信息、标识实体、标识每个实体需要存储的详细信息、标识实体之间的关系。 实体,就是指现实世界中具有区分其它事物的特征或属性,并与其他实体有联系的实体。实体一般是名...

Sql Server查询性能优化之走出索引的误区

据了解绝大多数开发人员对于索引的理解都是一知半解,局限于大多数日常工作没有机会、也什么没有必要去关心、了解索引,实在哪天某个查询太慢了找到查询条件建个索引就ok,哪天又有个查询慢了,再建立个索引就是,或者干脆把整个查询SQL直接发给DBA,让DBA直接帮忙优化了,所以造成的状况就是开发人员对于索引的理解、认识很局限,以下就把我个人对于索引的理解及浅薄认识...

SqlServer索引假脱机的解决

今天产品提出平台打开某一个模块速度特别慢,甚至有时会出现504的错误。赶紧连接正式版数据库本地调试代码,发现进行数据获取时,打开数据库和关闭数据库中间间隔的时间有5秒之久,这是数据量较少的情况,如果数据量更大的话就有可能出现错误了。看来问题是出在sql语句这里,把sql语句拷贝出来放到查询分析器中进行查看,速度还是很快的,但是查看执行计划的时候发现了问题,...

sql server百万级别数据量 农码一生

1.对查询进行优化,要尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引。 2.应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引而进行全表扫描,如: select id from t where num is null 最好不要给数据库留NULL,尽可能的使用 NOT NULL填充数...

SQL Server数据库查询速度慢的原因和解决方法

SQL Server数据库查询速度慢的原因有很多,常见的有以下几种:   1、没有索引或者没有用到索引(这是查询慢最常见的问题,是程序设计的缺陷)   2、I/O吞吐量小,形成了瓶颈效应。   3、没有创建计算列导致查询不优化。   4、内存不足   5、网络速度慢   6、查询出的数据量过大(可以采用多次查询,其他的方法降低数据量)   ...