SQL Server索引进阶:第七级,过滤的索引
原文地址:
Stairway to SQL Server Indexes: Level 7,Filtered Indexes
本文是SQL Server索引进阶系列(Stairway to SQL Server Indexes)的一部分。
在之前的级别中,我们已经说过,表中的每一行在索引中会生成一个入口,这条规则有一个例外。一些索引的入口会比对应的表的行数要少。这些索引被称作“过滤的索引”,是SQL Server 2008中的一个特性。
过滤一个索引
创建一个包含where子句的过滤的非聚集索引。
IF EXISTS ( SELECT *
FROM sys.indexes
WHERE OBJECT_ID = OBJECT_ID('Sales.SalesOrderDetail')
AND name = 'FI_SpecialOfferID' )
DROP INDEX Sales.SalesOrderDetail.FI_SpecialOfferID ;
GO CREATE INDEX FI_SpecialOfferID
ON Sales.SalesOrderDetail (SpecialOfferID)
WHERE SpecialOfferID;
过滤一个索引的主要原因,是为了从索引中消除一个或者多个不经常被选择的值。看一下SalesOrderDetail表中的SpecialOfferID列,121317行的数据包含了12个不同的SpecialOfferID值,从1到16,每个值对应的行数如下。
SpecialOfferID RowCount
-------------- -----------
1 115884
2 3428
3 606
13 524
14 244
16 169
7 137
8 98
11 84
4 80
9 61
5 2
大部分的行,超过95%,SpecialOfferID的值是1.在SpecialOfferID列的非聚集索引对于SpecialOfferID=1的查询没有好处。查询将使用表扫描来查询115884行数据。但是,所以对于SpecialOfferID=5的查询是有好处的。
本文开头的创建索引对于115884行SpecialOfferID=1的数据行没有入口。因此索引很小,很高效,只包含5433个入口。
在我们的SalesOrderDetail例子中,需要过滤的主要值是“1”。在你自己的应用中,最常见的可能是NULL。在典型的事务数据库中,在可空的列中,如果null值占多数,NOT NULL就是例外。在这些列上创建索引的时候,要考虑过滤null值。
概念证明
为了证明过滤索引的好处,我们将六次执行下面的查询:
- 三次在没有过滤的索引上执行,SpecialOfferID的值分别是:1,13,14。
- 三次在有过滤的索引上执行,参数和上面的一样。
SELECT *
FROM Sales.SalesOrderDetail
WHERE SpecialOfferID = parameter value
ORDER BY SpecialOfferID ;
在上面的统计中可以看出来,1的行数占95%,13的行数占4%,14的行数占2%。
和往常一样,我们使用IO读取次数作为主要的衡量指标,同时打开SQL Server管理器的“显示执行计划”选项,观察每次查询的执行计划。
执行结果的统计如下。
| WITH UNFILTERED INDEX: | ||
| Parameter Value | Reads | Plan |
| 1 | 1238 | Table scan |
| 13 | 1238 | Table scan |
| 14 | 758 | Retrieve bookmark values from index. Use them to retrieve rows from table |
| WITH FILTERED INDEX: | ||
| Parameter Value | Reads | Plan |
| 1 | 1238 | Table scan |
| 13 | 1238 | Table scan |
| 14 | 758 | Retrieve bookmark values from index. Use them to retrieve rows from table. |
从上面可以看出来吗,不管用没用到索引,结果是一样的。换句话说,过滤的索引和非过滤的索引带来的好处是一样的。在不影响查询效率的情况下,我们节省了大量的磁盘空间。
过滤,查询,覆盖
在上面的例子中,对同一个索引来说,过滤列和索引键列是同一个列。当我们通过where子句指定过滤的时候,我们告诉SQL Server:“如果你查询那些SpecialOfferID<>1的行,这个索引有这些行的入口。”不管索引键是什么,让SQL Server知道这些信息都是有好处的。
想一想我们之前创建的一个索引,帮助仓库管理员查询SalesOrderDetail表中产品相关的信息。
CREATE NONCLUSTERED INDEX FK_ProductID_ModifiedDate
ON Sales.SalesOrderDetail (ProductID,ModifiedDate)
INCLUDE (OrderQty,UnitPrice,LineTotal) ;
上面的索引产生的结果是。
:- Search Key Columns -: : --- Included Columns ---: :--- Bookmark ---:
ProductID ModifiedDate OrderQty UnitPrice LineTotal OrderId DetailId
----------- ------------ -------- --------- --------- ----------- ----------
Page n-1:
709 01 Feb 2002 1 5.70 5.70 45329 6392
709 01 May 2002 1 5.70 5.70 46047 8601
710 01 Jul 2001 1 5.70 5.70 43670 111
710 01 Jul 2001 1 5.70 5.70 43676 152
710 01 Sep 2001 1 5.70 5.70 44075 1448
Page n:
710 01 Oct 2001 1 5.70 5.70 44303 2481
710 01 Nov 2001 1 5.70 5.70 44484 2853
710 01 Nov 2001 1 5.70 5.70 44499 3006
710 01 Nov 2001 1 5.70 5.70 44523 3346
710 01 Nov 2001 1 5.70 5.70 44527 3400
如果仓库管理员经常查询SpecialOfferID<>1的信息,很少查询=1的信息,在创建索引的时候添加一个SpecialOfferID!=1的where子句是很有意义的。结果就是很小的索引就可以覆盖大部分的请求。
CREATE NONCLUSTERED INDEX FK_ProductID_ModifiedDate
ON Sales.SalesOrderDetail (ProductID,ModifiedDate)
INCLUDE (OrderQty,UnitPrice,LineTotal)
WHERE SpecialOfferID <>1
执行下面的查询语句。
SELECT ProductID ,
ModifiedDate ,
SUM(OrderQty) 'No of Items' ,
AVG(UnitPrice) 'Avg Price' ,
SUM(LineTotal) 'Total Value'
FROM Sales.SalesOrderDetail
WHERE SpecialOfferID <> 1
GROUP BY ProductID ,
ModifiedDate
SQL Server管理器告诉我们,过滤的索引被扫描,读取了36页,产生2102条结果。
一些警告
在决定使用过滤索引的时候,要记住两个重要的问题。
1 SQL Server如何评估过滤的索引
你可能会很奇怪,把之前查询语句的where条件从SpecialOfferID<>1变成SpecialOfferID=2,就会防止SQL Server使用过滤的索引。这是因为SQL Server比较了select查询的where子句和create index的where子句,认为他们两个语法上是相等的,而不是比较逻辑的相等。因此,SQL Server没有意识到过滤的索引覆盖了查询。
另外,你不能通过复合的where子句,例如:where SpecialOfferID<>1 and SpecialOfferID=2来促使SQL Server使用过滤的索引。在后面的级别中,我们将会给出一些提示,教给你一些影响SQL Server选择索引的能力。现在,记住SQL Server在评估过滤索引的时候,做出的是语法的决定。
2 不要使用过滤的索引来弥补不好的数据库设计
在创建过滤索引的时候,不要创建索引来弥补违反三范式的数据库设计。
大部分违反三范式的数据库设计,主要是对于实体的子类型认识错误。看一下下面的一张表。
| ProductID | Description | Type | Price | Author | IssuesPerYear |
| (Primary Key) | |||||
| 44E | Roots | Book | 44.50 | Alex Haley | |
| 17J | Time | Periodical | 18.00 | 52 | |
| 22D | Gift from the Sea | Book | 37.00 | Anne Morrow Lindbergh | |
| 18K | National Geographic | Periodical | 38.00 | 12 | |
| 78K | Good Housekeeping | Periodical | 37.00 | 12 |
很容易看出表中包含两种类型的商品:Book书和Periodical期刊。只有书才有Author作者信息,只有期刊才有IssuesPerYear每年的刊数信息。正确的方法是,一张主表来存放公共的信息,然后每个子类型附件一张表。每张表都有相同的主键,子类型表的主键也是连接主表的外键。
Products Table
| ProductID | Description | Price |
| (Primary Key) | ||
| 44E | Roots | 44.50 |
| 17J | Time | 18.00 |
| 22D | Gift from the Sea | 37.00 |
| 18K | National Geography | 38.00 |
| 78K | Good Housekeeping | 37.00 |
Books table
| ProductID | Author |
| (Primary Key and Foreign Key) | |
| 44E | Alex Haley |
| 22D | Anne Morrow Lindbergh |
Periodicals table
| ProductID | IssuesPerYear |
| (Primary Key and Foreign Key) | |
| 17J | 52 |
| 18K | 12 |
| 78K | 12 |
像上面的情况,可以建立过滤的索引,来过滤NULL列。
但是正确的做法是重新定义表结构。应用开发者和开发工具不知道你设计的索引,他们只能看见你的表。如果表的结构不影响业务的结构,开发者将会尽力的构建和维护数据库之上的应用。
结论
过滤的索引消除了索引中无用的入口,产生的索引更小,更有利于查询。过滤的索引是通过在create index中指定where子句来实现的。在where子句中的列不同于索引键的列,也不同于include子句中的列。
如果一张表中的一个子集经常被访问,过滤的索引也能是一个覆盖的索引,也可以导致IO有一个相当大的减少。
不要将创建过滤索引作为正确设计数据库的替代选择。
SQL Server索引进阶:第七级,过滤的索引的更多相关文章
- SQL Server调优系列基础篇(索引运算总结)
前言 上几篇文章我们介绍了如何查看查询计划.常用运算符的介绍.并行运算的方式,有兴趣的可以点击查看. 本篇将分析在SQL Server中,如何利用先有索引项进行查询性能优化,通过了解这些索引项的应用方 ...
- SQL Server 2016新特性:列存储索引新特性
SQL Server 2016新特性:列存储索引新特性 行存储表可以有一个可更新的列存储索引,之前非聚集的列存储索引是只读的. 非聚集的列存储索引支持筛选条件. 在内存优化表中可以有一个列存储索引,可 ...
- (转)Sql Server之旅——第八站 复合索引和include索引到底有多大区别?
索引和锁,这两个主题对我们开发工程师来说,非常的重要...只有理解了这两个主题,我们才能写出高质量的sql语句,在之前的博客中,我所说的 索引都是单列索引...当然数据库不可能只认单列索引,还有我这篇 ...
- SQL Server索引进阶:第十级,索引内部结构
原文地址: Stairway to SQL Server Indexes: Level 10,Index Internal Structure 本文是SQL Server索引进阶系列(Stairway ...
- SQL Server索引进阶第十一篇:索引碎片分析与解决
相关有关索引碎片的问题,大家应该是听过不少,也许也很多的朋友已经做了与之相关的工作.那我们今天就来看看这个问题. 为了更好的说明这个问题,我们首先来普及一些背景知识. 知识普及 我们都知道,数据库中的 ...
- SQL Server索引进阶:第一级,索引简介
这个并不是我翻译的,全文共有15篇,但我发现好多网站已经不全,所以自己整理. 原文地址: Stairway to SQL Server Indexes: Level 1, Introduction t ...
- SQL Server索引进阶第五篇:索引包含列 .
包含列解析所谓的包含列就是包含在非聚集索引中,并且不是索引列中的列.或者说的更通俗一点就是:把一些底层数据表的数据列包含在非聚集索引的索引页中,而这些数据列又不是索引列,那么这些列就是包含列.同时,这 ...
- SQL SERVER 自动生成 MySQL 表结构及索引 的建表SQL
SQL SERVER的表结构及索引转换为MySQL的表结构及索引,其实在很多第三方工具中有提供,比如navicat.sqlyog等,但是,在处理某些数据类型.默认值及索引转换的时候,总有些 ...
- Sql Server查询性能优化之走出索引的误区
据了解绝大多数开发人员对于索引的理解都是一知半解,局限于大多数日常工作没有机会.也什么没有必要去关心.了解索引,实在哪天某个查询太慢了找到查询条件建个索引就ok,哪天又有个查询慢了,再建立个索引就是, ...
- SQL Server性能优化(9)聚集索引的存储结构
一.索引的概念和分类 索引的概念大家都知道,日常开发中我们也会使用常见的聚集索引.非聚集索引.但是除了这两者以外,sqlserver中还提供其他的索引,如: a. 唯一索引:不包含重复键的索引,聚集索 ...
随机推荐
- Hash算法原理理解
我们有很多的小猪,每个的体重都不一样,假设体重分布比较平均(我们考虑到公斤级别),我们按照体重来分,划分成100个小猪圈. 然后把每个小猪,按照体重赶进各自的猪圈里,记录档案. 好了,如果我们要找某个 ...
- 【每天一个Linux命令】12. Linux中which命令的用法
which 用来查看可执行文件的位置. 1.命令格式: which 可执行文件名称 2.命令功能: which指令会在PATH变量指定的路径中,搜索某个系统命令的位置,并且返回第一个搜索结果. 3. ...
- 2016 Multi-University Training Contest 3 总结
又是多校总结时间. 这两天重感冒,精神不佳,总结一拖再拖,结果到了多校第四场结束后回来总结第三场.不过因为还在补第三场的题,所以还是记得挺清楚的 欣君说决定自己AFK试试,于是全程读题算公式. 欣君翻 ...
- [虚拟化/云][全栈demo] 为qemu增加一个PCI的watchdog外设(二)
这篇文章的理解,需要一些专业知识了. 我们可以创建模拟自己的外设吗? 我们已经知道什么是qemu了,我们可以通过qmeu的提供的外设,DIY一个计算机了. 但是我们可能还不满足,我们可以自己制造一个外 ...
- openStack images概念及维护
更改以创建镜像的属性 glance image-update img-uuid --property architecture=arm --propertyhypervisor_type=qemu C ...
- iphone5升级到iOS7时出现“This device isn't eligible for the requested build”错误
因为工作的需要我需要把自己的手机升级到iOS7,安装苹果的升级顺序总是报This device isn't eligible for the requested build错误,搜索相关的文章我的错误 ...
- 04737_C++程序设计_第6章_继承和派生
例6.1 使用默认内联函数实现单一继承. #include<iostream> using namespace std; class Point { private: int x, y; ...
- saiku中过滤窗口优化及隐藏异常报错
问题一:当取消自动查询后,点击该维度应弹出过滤条件窗口,实际无反应,只有执行一次查询后再点击该维度,才能弹出过滤条件窗口 解决办法:打开WorkspaceDropZone.js文件,找到selecti ...
- DBCP|C3P0参数详解
1.<!-- 数据源1 --> 2. <bean id="dataSource" 3. class="org.apache.commons.dbcp.B ...
- linux经常使用(一)linux 安装配置 jdk之 找不到安装文件文件夹及source /etc/profile 报unexpected end of file 错误 解决
linux 安装配置 jdk 应该算是一个非常主要的东西.可是我到如今才自己第一次 正式安装.果然出现了问题.. 问题就是 安装之后 找不到 安装路径 ,进而没法配置环境变量. 现象例如以下: 提示 ...