索引深入浅出(9/10):过滤索引

过滤索引(Filtered index )是在SQL Server 2008里新引入的功能。到目前我们谈到的索引都是在建立在整张表上的。换句话说,索引和表有一样的记录树。使用过滤索引,我们可以创建表子集的索引。这个可以通过创建索引的时候加上where子语完成。这个可以帮助在存储上减小索引的大小同样索引的深度。在索引创建语句的where条件决定了索引里是否包含该记录。

这在大表上是一个巨大的性能提升,如果有大量的查询只查表的部分数据。常规索引都是建立在整个表上的,而忽略了大多数查询只查表的一部分数据的事实。这样话常规索引会增加索引的深度,需要更多的页来存储B树结构,从而带来更多的IO操作。而过滤索引只在表的部分数据上创建,因此需要更少的页来存储索引。

我们以有5年数据的salesorder 表来举例。这个表上大多数活跃的查询是基于去年和今年的。过滤索引的简单例子如下:

1 CREATE NONCLUSTERED INDEX ix_salesorder_Filter
2 ON salesorder(SalesOrderId,OrderDate,Status,Customer_id,TotalDue)
3 WHERE OrderDate>‘2014-01-01‘ 

有多个NULL值的唯一列:

过滤索引的一个大量使用是在列上复合唯一限制定义。这个唯一限制,不允许在唯一列上有多个null值。列上的值除去NULL值,怎么保证它其它值唯一?例如,product表上的productode列可以为null,但是那列有值定义的话,就不能为null。我们来看一个例子。

 1 CREATE TABLE Product
 2     (
 3       Productid INT NOT NULL  PRIMARY KEY ,
 4       ProductCode CHAR(10) ,
 5       ProductName VARCHAR(100)
 6     )
 7 GO
 8 CREATE UNIQUE INDEX ix_Unique_Filtered ON Product(Productcode) WHERE productcode IS NOT NULL
 9 GO
10 INSERT INTO Product VALUES(1,‘AR-5381‘,‘Adjustable Race‘)
11 INSERT INTO Product VALUES(2,NULL,‘Bearing Ball‘)
12 INSERT INTO Product VALUES(3,NULL,‘BB Ball Bearing‘)
13 INSERT INTO Product VALUES(4,‘AR-5381‘,‘Adjustable Race-Small‘)

我们可以插入produtcode列为NULL的多条记录,但当我们尝试插入有重复值AR-5381的记录是,SQL Server提示错误,那是因为表上的唯一过滤索引帮助我们强制这样的唯一性。

读操作:

上述提高的是覆盖索引的一种常规用法。覆盖索引的另一种用法是支持对应的查询,我们来看一个例子,点击工具栏的显示包含实际的执行计划。

 1 USE IndexDB
 2 GO
 3 SELECT * INTO SalesOrderheader FROM AdventureWorks2008r2.Sales.SalesOrderheader
 4 GO
 5 --Unique Clustered index
 6 CREATE UNIQUE CLUSTERED INDEX ix_SalesOrderheader ON SalesOrderheader(SalesOrderid)
 7 GO
 8 --Filtered Index
 9 CREATE INDEX ix_filtered_index ON SalesOrderheader(orderdate) WHERE orderdate>‘2008-01-01‘
10
11 SELECT orderdate,SalesOrderid FROM SalesOrderheader WHERE
12 orderdate>‘2008-05-01‘
13 GO
14 SELECT orderdate,SalesOrderid FROM SalesOrderheader WHERE
15 orderdate=‘2008-03-01‘
16 GO
17 SELECT orderdate,SalesOrderid FROM SalesOrderheader WHERE
18 orderdate=‘2007-12-01‘

我们在orderdate列上定义了过滤索引,条件是日期大于2008-1-1。第一个和第二个查询条件都命中了过滤索引的条件,因此都用到了索引查找;第三个在覆盖索引条件之外,SQL Server只能用聚集索引扫描来找对应的数据。过滤索引对于这类查询有大幅度的性能提升。

过滤列并不一定是索引的一部分。但如果是这个情况的话,查询条件就要和过滤索引的条件完全一致了,我们来看一个例子。

1 CREATE INDEX ix_TerritoryID_Filter ON SalesOrderheader (OrderDate) WHERE TerritoryID<=5
2 GO
3 SELECT salesorderid,orderdate FROM SalesOrderheader WHERE
4 TerritoryID<=5
5 GO
6 SELECT salesorderid,orderdate FROM SalesOrderheader WHERE
7 TerritoryID=4

覆盖索引局限性:

覆盖索引非常有用,但也有它的局限性,尤其是在参数化查询的时候。我们来看一个例子。

1 SELECT orderdate,SalesOrderid FROM SalesOrderheader WHERE orderdate>‘2008-05-01‘
2
3 DECLARE @Orderdate date=‘2008-05-01‘
4 SELECT orderdate,SalesOrderid FROM SalesOrderheader WHERE orderdate>@Orderdate 

当我们用本地参数重写我们的查询的时候,查询计划没有用到过滤索引。背后的原因是,在编译期间,查询优化器不知道什么样的值会传给@Orderdate参数。因此优化器生成一个保守的计划来满足所有的条件。当我们修改数据库属性来强制参数化或把语句定义为存储过程也会出现同样的问题。

 1 ALTER DATABASE IndexDB SET parameterization forced
 2 GO
 3 SELECT orderdate,SalesOrderid FROM SalesOrderheader WHERE orderdate>‘2008-05-01‘
 4 GO
 5
 6 CREATE PROCEDURE GetSalesorder (@OrderDate date)
 7 AS
 8 BEGIN
 9 SELECT orderdate,SalesOrderid FROM SalesOrderheader WHERE orderdate=@OrderDate END
10
11
12 EXEC GetSalesorder ‘2008-05-01‘

在这些情况下优化器都没有用到过滤索引,因为它在执行的时候不知道传入的值是多少,只能不考虑使用过滤索引生成一个相对保守的计划。

当我们把数据库属性设置为强制参数化时(ALTER DATABASE IndexDB SET parameterization forced),优化器会把所有静态值用本地变量代替。例如下面的语句:

1 SELECT orderdate,SalesOrderid FROM SalesOrderheader WHERE orderdate>‘2008-05-01‘

优化器会认为是下列语句:

1 DECLARE @Orderdate date=‘2008-05-01‘
2 SELECT orderdate,SalesOrderid FROM SalesOrderheader WHERE orderdate>@Orderdate

但在动态生成的语句时,会让SQL Server用到过滤索引:

1 ALTER DATABASE IndexDB SET PARAMETERIZATION SIMPLE
2
3 DECLARE @Orderdate date=‘2008-05-01‘
4 DECLARE @SQL NVARCHAR(1000)
5 SET @SQL=N‘SELECT orderdate,SalesOrderid FROM SalesOrderheader WHERE
6 orderdate>‘‘‘+CAST(@Orderdate AS CHAR(10))+‘‘‘‘
7 EXEC (@SQL)
8 GO 

从执行计划里我们可以看到,查询计划用到了过滤索引。

时间: 2024-11-06 09:25:27

索引深入浅出(9/10):过滤索引的相关文章

索引深入浅出:非聚集索引的B树结构在聚集表

一个表只能有一个聚集索引,数据行以此聚集索引的顺序进行存储,一个表却能有多个非聚集索引.我们已经讨论了聚集索引的结构,这篇我们会看下非聚集索引结构. 非聚集索引的逻辑呈现 简单来说,非聚集索引是表的子集.当我们定义了一个非聚集索引时,SQL Server把整套非聚集索引键存在不同的页里.我们来看下一个包含BusinessEntityID(PK),PersonType,FirstName,LastName这4列的表,这个表上有一个非聚集索引定义.主体表按BusinessEntityID列(聚集索引

索引深入浅出:非聚集索引的B树结构在堆表

在“索引深入浅出:非聚集索引的B树结构在聚集表”里,我们讨论了在聚集表上的非聚集索引,这篇文章我们讨论下在堆表上的非聚集索引. 非聚集索引可以在聚集表或堆表上创建.当我们在聚集表上创建非聚集索引时,聚集索引键担当为行指针.在堆表里,文件号,页号和槽号(file id , page number and slot number)的组合在非聚集索引里担当为行指针. 我们来看下手头的一个例子.我们创建salesorderdetail表的副本,并在上面的productid和salesorderid 列创

SQL Server-聚焦过滤索引提高查询性能(十)

前言 这一节我们还是继续讲讲索引知识,前面我们聚集索引.非聚集索引以及覆盖索引等,在这其中还有一个过滤索引,通过索引过滤我们也能提高查询性能,简短的内容,深入的理解. 过滤索引,在查询条件上创建非聚集索引(1) 过滤索引是SQL 2008的新特性,被应用在表中的部分行,所以利用过滤索引能够提高查询,相对于全表扫描它能减少索引维护和索引存储的代价.当我们在索引上应用WHERE条件时就是过滤索引.也就是满足如下格式: CREATE NONCLUSTERED INDEX <index name> O

第十二章——SQLServer统计信息(4)——在过滤索引上的统计信息

原文:第十二章--SQLServer统计信息(4)--在过滤索引上的统计信息 前言: 从2008开始,引入了一个增强非聚集索引的新功能--过滤索引(filter index),可以使用带有where条件的语句来创建非聚集索引,过滤掉不需要的数据,降低索引的维护开销和存储空间,提高查询性能. 准备工作: 在AdventureWorks2012上,有一个Production.WorkOrder表,将使用这个表来做演示. 步骤: 1.  创建一个非聚集索引在Production.WorkOrder列:

索引深入浅出:索引深入浅出的聚集索引页

本篇是“索引深入浅出”的开篇,也是这个系列文章的聚集索引页,下面文章标题排列顺序将和对应文章发布顺序一致: 索引简介:索引概述,通过现实生活实例理解聚集索引.非聚集索引还有键查找(Key Look up). 堆表:没有聚集索引的表叫堆表.在堆表中的数据存储没有任何顺序可言. 聚集索引的B树结构:有聚集索引的表叫聚集表.聚集索引使用B树结构的聚集健顺序,存储实际的数据.一个表只能有一个聚集索引.我们会谈到聚集索引的存储结构,还有对应访问方式. 非聚集索引的B树结构在聚集表:一个表可以创建多个非聚集

SQL Server索引内部结构:SQL Server索引的阶梯级别10

作者David Durant,2012年1月20日 该系列 本文是"Stairway系列:SQL Server索引的阶梯"的一部分 索引是数据库设计的基础,并告诉开发人员使用数据库关于设计者的意图.不幸的是,当性能问题出现时,索引往往被添加为事后考虑.这里最后是一个简单的系列文章,应该使他们快速地使任何数据库专业人员"快速" 在之前的水平上,我们采取了合理的方法来指标,重点是他们能为我们做些什么.现在是时候采取物理方法,检查指标的内部结构;了解索引的内部特性导致了对

1.22 P235第一题将1—100之间的正整数放在List集合中,并将索引位置是10的对象从集合移除。

1 public class List1 { 2 3 4 public static void main(String[] args) { 5 6 //array 7 8 ArrayList<Integer> list = new ArrayList<Integer>();//创建list 集合 9 10 for(int i=1;i<=100;i++) //list 添加i 11 { 12 list.add(i); 13 } 14 list.remove(10); //移除索

索引深入浅出:聚集索引

在SQL Server里,有2种表是以存储为基础的.有聚集索引的表叫聚集表,没有聚集索引的表叫堆表.在上一篇文章,我们讨论了堆表的特性和存储结构.在这篇文章里,我们来看下聚集表. 有聚集索引的表叫聚集表.聚集索引保存了使用B树结构的聚集键,并只能以此顺序存储实际的数据.这也是SQL Server限制一个表只能有一个聚集索引,因为物理存储顺序只能有一个.我们来看看B树结构的逻辑呈现.下图是基于AdventureWorks2008R2数据库,表SalesOrderDetail创建的. 1 USE I

09.索引-过滤索引

USE IndexDB GO SELECT * INTO SalesOrderheader FROM AdventureWorks2008r2.Sales.SalesOrderheader GO --Unique Clustered index CREATE UNIQUE CLUSTERED INDEX ix_SalesOrderheader ON SalesOrderheader(SalesOrderid) GO --Filtered Index CREATE INDEX ix_filtere