17xie > Microsoft SQL Server 2005技术内幕 > 1.7 索引视图(Indexed View)
背景:                 
[本书目录] [图书首页] [本书讨论区]  
链接地址:http://www.17xie.com/read-39348.html    注册17xie 一起来写书 实现您的出书梦想!

1.7  索引视图(Indexed View)

如果没有索引,视图中的数据不会有任何物理的表示形式。相反,它只包含基表的元数据信息。然而,如果你在视图上创建唯一聚集索引,SQL Server将物理化(materialize)视图中的数据。SQL Server会在修改基础表时同步索引视图。但你不能直接同步视图的内容。在数据完整性方面,索引视图与表索引非常相似。

索引视图在查询数据时可以极大地提高性能。它可以显著降低返回数据所请求的I/O数量以及执行计算所需的处理时间。例如,对于数据聚合(data aggregation)查询或高成本的联接来说,利用索引视图可以显著地提高性能。但是要记住,对索引视图基础表的修改需要更新索引视图,从而降低了修改的性能。

创建索引视图有许多要求和限制,这使得我们会经常因此而放弃使用它。SQL Server 2005中对索引视图的要求并没有减少。

在视图上创建的第一个索引必须是唯一聚集索引。在视图上创建聚集索引后,你就可以创建其他的非聚集索引了。视图必须使用SCHEMABINDING选项创建。因此,必须为对象名称使用两部分命名约定,并在SELECT列表中显式指定列的名称。如果视图中的查询对数据执行聚合,它的SELECT列表必须包含COUNT_BIG(*)聚合函数。COUNT_BIG与COUNT相同,只不过它的结果类型是BIGINT。该统计允许SQL Server跟踪每组被聚合的行数,并用于计算其他的聚合。会话中的一些SET选项必须是某一状态。要求和限制还有很多,更详细的信息请参考联机丛书。

例如,假设你的查询从Orders 和 Order Details表请求员工的聚合数据,你想优化这个查询。一种方法是创建一个物理化的视图,该视图包含你要请求的聚合。下面的代码创建一个索引视图VempOrders,它的查询联接Orders 和 Order Details,按EmployeeID分组,并计算每个员工的Quantity合计和行数。

USE Northwind;

GO

IF OBJECT_ID(‘dbo.VempOrders’) IS NOT NULL

  DROP VIEW dbo.VempOrders;

GO

CREATE VIEW dbo.VempOrders WITH SCHEMABINDING

AS

SELECT O.EmployeeID, SUM(OD.Quantity) AS TotalQty, COUNT_BIG(*) AS Cnt

FROM dbo.Orders AS O

  JOIN dbo.[Order Details] AS OD

ON OD.OrderID = O.OrderID

GROUP BY O.EmployeeID;

GO

CREATE UNIQUE CLUSTERED INDEX idx_uc_empid ON dbo.VempOrders(EmployeeID);

GO

要注意创建视图时使用了SCHEMABINDING选项,并使用两部分名称引用表,因为该查询计算聚合,所以使用了COUNT_BIG函数,而且在视图上创建的索引是唯一聚集索引。

当基础表被修改时,SQL Server并不重新生成整个索引,它用一种很巧妙的方式维护索引。当你插入数据时,SQL Server将标识这条受影响的行并增加该行的聚合值TotalQtyCnt。当你删除数据时,SQL Server减去相应值。当更新基础表的数据时,SQL Server会相应地更新聚合值。

要想观察索引视图给你带来的性能提升,打开STATISTICS IO选项和SSMS中的包含实际执行计划选项后,运行下面的查询。

SELECT EmployeeID, TotalQty, Cnt FROM dbo.VempOrders;

这个查询的输出如表5-11所示,图5-3显示了它的执行计划。

表5-11  VempOrders 的内容

EmployeeID

TotalQty

Cnt

1

7812

345

2

6055

241

3

7852

321

4

9798

420

5

3036

117

6

3527

168

7

4654

176

8

5913

260

9

2670

107

图5-3  查询VempOrders视图的执行计划

该计划对视图的聚集索引进行了扫描。对于这个只包含9行的视图,I/O成本是2次逻辑读取。如果你使用SQL Server的企业版(或开发版,功能一样),如果查询该视图时未指定任何提示或直接查询基表,查询优化器将考虑使用索引视图。例如,下面的查询生成如图5-4所示的执行计划。

SELECT O.EmployeeID, SUM(OD.Quantity) AS TotalQty, AVG(OD.Quantity) AS AvgQty, COUNT_BIG(*)

AS Cnt

FROM dbo.Orders AS O

  JOIN dbo.[Order Details] AS OD

ON OD.OrderID = O.OrderID

GROUP BY O.EmployeeID;

图5-4  查询Orders和Order Details表的执行计划

如你所见,查询使用了索引视图,它的I/O成本只有2次逻辑读取。有意思的是,查询请求了聚合函数AVG(OD.Quantity),而它不是该视图的一部分,但依然使用了索引视图。查询还计算了数量合计数和行数。如果展开计划中Compute Scalar的属性,你会发现下面的表达式,它用于计算合计数和行数的平均值。

[Expr1005] = CASE WHEN [Northwind].[dbo].[VempOrders].[Cnt]=(0) THEN NULL ELSE [Northwind].[dbo].[VempOrders].[TotalQty]/

CONVERT_IMPLICIT(int,[Northwind].[dbo].[VempOrders].[Cnt],0) END, [[Northwind].[dbo]. [VempOr

ders].Cnt] = [Northwind].[dbo].[VempOrders].[Cnt]

注意   如果你使用的不是SQL Server企业版或开发版,默认情况下,即使你直接查询视图,也不会考虑使用索引视图。这时,要使用索引视图,你必须指定NOEXPAND提示。

我前面提到过,SQL Server 2005并未减少创建索引视图的条件和限制。但在SQL Server 2005中可以在更多的情况下使用索引视图,从这个意义上来讲,优化器被增强。例如,当外部查询指定的查询筛选器是视图的筛选器的子区间(subinterval)时,SQL Server 2000不使用索引视图。也就是说,如果视图的查询包含筛选器col1>5,而外部查询包含筛选器col1>10,则根本不会考虑使用索引视图。SQL Server 2005现在支持子区间。同样,当外部查询使用一个与视图逻辑上等效但不一定完全匹配的筛选表达式时,SQL Server 2000不会使用索引视图。例如,如果视图的查询包含筛选器col1=5而外部查询的筛选器为5=col1,SQL Server 2000的优化器不考虑使用索引视图。但SQL Server 2005优化器则相反。

更多信息   关于SQL Server 2005中的索引视图以及对SQL Server 2000中索引视图的改进的更多信息,请参考Eric Hanson撰写的白皮书“Improving Performance with SQL Server 2005 Indexed Views”,地址为:http://www.microsoft.com/ technet/ prodtechnol/sql/2005/ipsql05iv.mspx

除了性能,你还可能会因为其他原因使用索引视图。例如,T-SQL的UNIQUE约束会认为两个NULL相等。如果你在一个可为空的列上创建UNIQUE约束,该约束将只允许该列出现一个NULL值。假设你只想强制已知值(即非NULL值)的唯一性,允许出现多个NULL。虽然你可以使用触发器实现,但这样的触发器是需要成本的。如果触发器回滚,触发操作就好像执行了两次,或者更准确地说,是一次完成和一次撤消。如果不使用触发器,你可以使用索引视图来强制完整性规则(integrity rule)。从一个查询创建索引视图,而该查询从原始列中筛选非NULL值。前面曾提到,视图上创建的聚集索引必须是唯一的。这种索引将防止重复的已知值进入基表,但允许多个NULL,因为NULL不是唯一索引的一部分。

为演示这一点,运行下面的代码,它将创建包含keycol列的表T1,以及一个从T1中过虑已知keycol值的索引视图。

USE tempdb;

GO

IF OBJECT_ID(‘dbo.V1’) IS NOT NULL

  DROP VIEW dbo.V1;

GO

IF OBJECT_ID(‘dbo.T1’) IS NOT NULL

  DROP TABLE dbo.T1;

GO

CREATE TABLE dbo.T1

(

  keycol  INT         NULL,

  datacol VARCHAR(10) NOT NULL

);

GO

CREATE VIEW dbo.V1 WITH SCHEMABINDING

AS

SELECT keycol FROM dbo.T1 WHERE keycol IS NOT NULL;

GO

CREATE UNIQUE CLUSTERED INDEX idx_uc_keycol ON dbo.V1(keycol);

接下来,执行下面的INSERT语句:

INSERT INTO dbo.T1(keycol, datacol) VALUES(1,    ‘a’);

INSERT INTO dbo.T1(keycol, datacol) VALUES(1,    ‘b’); -- 失败

INSERT INTO dbo.T1(keycol, datacol) VALUES(NULL, ‘c’);

INSERT INTO dbo.T1(keycol, datacol) VALUES(NULL, ‘d’);

注意第二条语句尝试插入keycol值为1的行将失败,但两个NULL可以被接受。查询T1并观察表5-12中的输出可以看到,两个NULL都被插入到该表中。

SELECT keycol, datacol FROM dbo.T1;

表5-12  T1的内容

keycol

datacol

1

a

NULL

c

NULL

d

完成后,运行下面的代码进行清理:

USE Northwind;

GO

IF OBJECT_ID(‘dbo.VempOrders’) IS NOT NULL

  DROP VIEW dbo.VempOrders;

GO

USE tempdb;

GO

IF OBJECT_ID(‘dbo.V1’) IS NOT NULL

  DROP VIEW dbo.V1;

GO

IF OBJECT_ID(‘dbo.T1’) IS NOT NULL

  DROP TABLE dbo.T1;

GO


字数:5007    最后更新:8个月以前 [03-18 15:20]happyskynet 修改
本页编辑者:happyskynet  
[前一页]:1.6 视图选项  [后一页]:1.8 结论
[在本页中加入书签] [收藏本书] [推荐本书]
  17xie论坛 > 本书讨论区 > 本页评论   (共0条)
发表评论

用户名称 匿名发表
评论内容
验证码

关于我们 | 版权声明 | 免责声明 | 诚聘英才 | 联系我们 | 合作伙伴 | 友情链接 | 广告合作 | 提交意见
Copyright © 2007 17xie.com 互联网协同写书平台 京ICP备08002671号