原文地址:

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索引进阶:第七级,过滤的索引的更多相关文章

  1. SQL Server调优系列基础篇(索引运算总结)

    前言 上几篇文章我们介绍了如何查看查询计划.常用运算符的介绍.并行运算的方式,有兴趣的可以点击查看. 本篇将分析在SQL Server中,如何利用先有索引项进行查询性能优化,通过了解这些索引项的应用方 ...

  2. SQL Server 2016新特性:列存储索引新特性

    SQL Server 2016新特性:列存储索引新特性 行存储表可以有一个可更新的列存储索引,之前非聚集的列存储索引是只读的. 非聚集的列存储索引支持筛选条件. 在内存优化表中可以有一个列存储索引,可 ...

  3. (转)Sql Server之旅——第八站 复合索引和include索引到底有多大区别?

    索引和锁,这两个主题对我们开发工程师来说,非常的重要...只有理解了这两个主题,我们才能写出高质量的sql语句,在之前的博客中,我所说的 索引都是单列索引...当然数据库不可能只认单列索引,还有我这篇 ...

  4. SQL Server索引进阶:第十级,索引内部结构

    原文地址: Stairway to SQL Server Indexes: Level 10,Index Internal Structure 本文是SQL Server索引进阶系列(Stairway ...

  5. SQL Server索引进阶第十一篇:索引碎片分析与解决

    相关有关索引碎片的问题,大家应该是听过不少,也许也很多的朋友已经做了与之相关的工作.那我们今天就来看看这个问题. 为了更好的说明这个问题,我们首先来普及一些背景知识. 知识普及 我们都知道,数据库中的 ...

  6. SQL Server索引进阶:第一级,索引简介

    这个并不是我翻译的,全文共有15篇,但我发现好多网站已经不全,所以自己整理. 原文地址: Stairway to SQL Server Indexes: Level 1, Introduction t ...

  7. SQL Server索引进阶第五篇:索引包含列 .

    包含列解析所谓的包含列就是包含在非聚集索引中,并且不是索引列中的列.或者说的更通俗一点就是:把一些底层数据表的数据列包含在非聚集索引的索引页中,而这些数据列又不是索引列,那么这些列就是包含列.同时,这 ...

  8. SQL SERVER 自动生成 MySQL 表结构及索引 的建表SQL

          SQL SERVER的表结构及索引转换为MySQL的表结构及索引,其实在很多第三方工具中有提供,比如navicat.sqlyog等,但是,在处理某些数据类型.默认值及索引转换的时候,总有些 ...

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

    据了解绝大多数开发人员对于索引的理解都是一知半解,局限于大多数日常工作没有机会.也什么没有必要去关心.了解索引,实在哪天某个查询太慢了找到查询条件建个索引就ok,哪天又有个查询慢了,再建立个索引就是, ...

  10. SQL Server性能优化(9)聚集索引的存储结构

    一.索引的概念和分类 索引的概念大家都知道,日常开发中我们也会使用常见的聚集索引.非聚集索引.但是除了这两者以外,sqlserver中还提供其他的索引,如: a. 唯一索引:不包含重复键的索引,聚集索 ...

随机推荐

  1. C++ 一些容易忽略的基本点

    new 高级应用 delete 其他用途帮助函数严格匹配类型 char teststr[100] = { 0 };// 静态区void test3(){ // 堆区 int *p = new int[ ...

  2. android DatePickerDialog theme

    网上搜索了下没有找到DatePickerDialog的各种 Theme 的样例.我就一个一个试了下,传上图片 DatePickerDiaolog有两个构造函数分别是: DatePickerDialog ...

  3. 占成本85% SSD深度选购教你如何看颗粒

    颗粒是固态硬盘负责容量和传输的介质,在这一方面上与优盘产品是相同的,从外观上看,颗粒占据了整个固态硬盘内部70%左右的空间,其同样做为成本技术,根据厂商的用料不同,成为了固态硬盘内部核心材料. 颗粒的 ...

  4. 利用aspose.cell把数据导出到excel

    /// <summary> /// 导出数据到本地 /// </summary> /// <param name="dt">要导出的数据< ...

  5. 7. Reverse Integer

    1. 问题描述 Reverse digits of an integer.Example1: x = 123, return 321Example2: x = -123, return -321 cl ...

  6. 【Java并发编程】并发编程大合集-值得收藏

    http://blog.csdn.net/ns_code/article/details/17539599这个博主的关于java并发编程系列很不错,值得收藏. 为了方便各位网友学习以及方便自己复习之用 ...

  7. SET QUOTED_IDENTIFIER (Transact-SQL)

    使 SQL Server 遵从关于引号分隔标识符和文字字符串的 ISO 规则. 由双引号分隔的标识符可以是 Transact-SQL 保留关键字,也可以包含 Transact-SQL 标识符语法约定通 ...

  8. debug jdk

    1.解压jdk安装路径中的src.zip如:c:\src. 注意一般只选择编译java javax org 三个文件夹就足够了,剩余的文件夹删除掉 2.列出要编译的源文件:dir /B /S /X c ...

  9. VS2013 快捷键乱掉如何修改回来

    比如 CTRL+E+C =注释 F6=重新生成解决方案 CTRL+D+Q=运行时快速监视 工具-->选项-->环境-->键盘-->应用以下其他键盘映射方案,下拉选择 Visua ...

  10. org.apache.solr.client.solrj.impl.HttpSolrServer$RemoteSolrException: Internal Server Error 错误

    Solr报错: { "responseHeader": { "status": 500, "QTime": 11 }, "erro ...