SqlServer中Index Seek的匹配规则(一)
我们知道在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的匹配规则(一)的更多相关文章
- SqlSever中Index Seek的匹配规则(一)
我们知道在SqlServer中,索引对查询语句的优化起着巨大的作用,一般来说在执行计划中出现了Index Seek的步骤,我们就认为索引命中了.但是Index Seek中有两个部分是值得我们注意的,我 ...
- Android中的Intent Filter匹配规则介绍
本文主要介绍了隐式Intent匹配目标组件的规则,若有叙述不清晰或是不准确的地方希望大家指出,谢谢大家: ) 1. Intent简介 Intent用于在一个组件(Component,如Activity ...
- SqlServer nvarchar中的中文字符匹配,更改SqlServer实例和数据库排序规则的办法
我们都知道在SqlServer中的nvarchar类型可以完美的存储诸如中文这种unicode字符,但是我们会发现有时候查询语句去查询nvarchar列的时候查不出来. 为什么nvarchar类型有时 ...
- Sql Server中的表访问方式Table Scan, Index Scan, Index Seek
1.oracle中的表访问方式 在oracle中有表访问方式的说法,访问表中的数据主要通过三种方式进行访问: 全表扫描(full table scan),直接访问数据页,查找满足条件的数据 通过row ...
- 转: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 ...
- web.xml中url-pattern匹配规则
小知识 一般的URL组成 URL = 服务器地址 + RequestURI 例如URI:http://localhost:8080/practice/main [http://localhost:80 ...
- Go的http包中默认路由匹配规则
# 一.执行流程 首先我们构建一个简单http server: ```go package main import ( "log" "net/http" ) f ...
- 在JaveWeb项目中配置Spring 匿名访问时,匹配规则的变相实现/*
实现/* /** * 根据当前的URL返回该url的角色集合. * 1.如果当前的URL在匿名访问的URL集合当中时,在当前的角色中添加匿名访问的角色(SysRole.ROLE_CONFIG_ANON ...
- Web.xml中设置Servlet和Filter时的url-pattern匹配规则
一.servlet容器对url的匹配过程: 当一个请求发送到servlet容器的时候,容器先会将请求的url减去当前应用上下文的路径作为servlet的映射url,比如我访问的是http://loca ...
随机推荐
- chrome 等浏览器不支持本地ajax请求,的问题
XMLHttpRequest cannot load file:///D:/WWW/angularlx/ui-router-test/template/content.html. Cross orig ...
- C++中的break、continue、goto语句
break.continue.goto break用于提前结束循环.只能打断一层循环.是把一层循环全部结束掉.continue则是提前结束循环内单次,继续循环下一步.
- WebMercator和geographic互相转换
方法1:esri的sdk中包含的方法:esri.geometry.geographicToWebMercator() 方法2:自己转换 //经纬度转Web墨卡托 function lonLat2Web ...
- 网页开启QQ会话
在网页中开启QQ会话是很常见的,那么到底怎么调用了. 一.首先要被开启的QQ开启推广服务.注意这个一般开启难免被骚扰,所以不要用生活号. 传送门:https://www.xinxiangseo.cn/ ...
- ES6深入浅出-4 迭代器与生成器-4.总结
yield的值就是外面调用next得到的值 ES6给的新的语法,如果你给任何一个对象添加一个Symbol.interator的key,同时它的值是一个生成器. 下面选中的就是生成器.生成返回的东西是迭 ...
- phpexcel 导出excel无法打开,提示文件格式或文件名无效,文件损毁,解决办法
使用过很多次phpexcel了,有时需要保存文件到磁盘,有时需要浏览器弹出下载.保存到磁盘一半不会出现问题,关键是浏览器弹出保存,经常会发生导出的excel文件无法打开,提示文件格式或文件名无效,文件 ...
- Day1作业1:登陆接口(加入日志、注册功能)
流程图如下: 最先考虑使用python中的list,以能取到user_list中的用户信息,但中途发现没有比较好的方法截取取密码,还是新手的缘故,最终选择了使用dict,以方便截取用户名以及相应的密码 ...
- 打乱数组 shuffle
<?php $arr = range(,); print_r($arr); echo '<br />'; shuffle($arr); print_r($arr); ?> Ar ...
- Configuration system failed to initialize
引用:https://cloud.tencent.com/developer/article/1336954 重装.net Framework
- linux 大量time_wait的解决方法
通过调整内核参数解决vi /etc/sysctl.conf 编辑文件,加入以下内容:net.ipv4.tcp_syncookies = 1net.ipv4.tcp_tw_reuse = 1net.ip ...