设计索引视图
视图也称为虚拟表,因为视图所返回的结果集的一般格式与表相同,都是由列和行组成,而且在 SQL 语句中引用视图的方式也与引用表的方式相同。标准视图的结果集不是永久地存储在数据库中。每次查询引用标准视图时,SQL Server 都会在内部将视图的定义替换为该查询,直到修改后的查询仅引用基表。然后,它将照常运行所得到的查询。有关详细信息,请参阅视图解析。
对于标准视图而言,为每个引用视图的查询动态生成结果集的开销很大,特别是对于那些涉及对大量行进行复杂处理(如聚合大量数据或联接许多行)的视图。如果在查询中频繁地引用这类视图,可通过对视图创建唯一聚集索引来提高性能。对视图创建唯一聚集索引后,结果集将存储在数据库中,就像带有聚集索引的表一样。
对视图创建索引的另一个好处是:优化器可以在未直接在 FROM 子句中指定某一视图的查询中使用该视图的索引。这样一来,可从索引视图检索数据而无需重新编码,由此带来的高效率也使现有查询获益。有关详细信息,请参阅解析视图的索引。
对基表中的数据进行更改时,数据更改将反映在索引视图中存储的数据中。视图的聚集索引必须唯一,这一要求提高了 SQL Server 在索引中查找受任何数据更改影响的行的效率。
和早期版本相此,当查询和视图定义中都包含下列匹配元素时,查询优化器在处理查询时能更充分地发挥索引视图的作用:
标量表达式。例如,查询优化器可将谓词中包含标量表达式的以下查询:
SELECT ColA, ColB FROM TableT WHERE ColC * (ColD + 10) > 50
与对此视图创建的索引相匹配:
CREATE VIEW V1 WITH SCHEMABINDING AS SELECT ColA, ColB, ColC * (ColD + 10) AS ExpCol FROM dbo.TableT
包含用户定义函数的标量表达式也可以通过类似的方式进行匹配。
标量聚合函数。例如,在 SELECT 列表中包含标量聚合函数的以下查询:
SELECT COUNT_BIG (*) FROM dbo.TableT
可以与对此视图创建的索引相匹配:
CREATE VIEW V2 WITH SCHEMABINDING AS SELECT COUNT_BIG (*) AS Cnt FROM dbo.TableT
当查询优化器选择查询计划时,它还会考虑下列因素:
查询谓词中定义的间隔值是否位于索引视图中定义的间隔内。例如,请考虑对以下视图创建的索引:
CREATE VIEW V1 WITH SCHEMABINDING AS SELECT ColA, ColB, ColC FROM dbo.TableT WHERE ColA > 1 and ColA < 10
现在请考虑以下查询:
SELECT ColB, ColC FROM dbo.TableT WHERE ColA > 3 and ColA < 7
查询优化器将此查询与视图 V1 匹配,因为查询中定义的 3 到 7 的间隔位于索引视图中定义的 1 到 10 的间隔内。
表达式在查询中的定义程度与在索引视图中相当。SQL Server 通过考虑表达式的列引用、文本、逻辑运算符 AND、OR、NOT、BETWEEN 和 IN,以及比较运算符 =、<>、>、<、>= 和 <= 来尝试匹配表达式。不考虑算术运算符(例如 + 和 %)及参数。
例如,查询优化器将以下查询:
SELECT ColA, ColB from dbo.TableT WHERE ColA < ColB
与对此视图创建的索引相匹配:
CREATE VIEW V1 WITH SCHEMABINDING AS SELECT ColA, ColB FROM dbo.TableT WHERE ColB > ColA
请记住,因为这对所有索引都适用,所以只有当查询优化器确定使用索引视图会有所帮助时,SQL Server 才会选择在其查询计划中使用索引视图。
索引视图可以在 SQL Server 2008 的任何版本中创建。在 SQL Server 2008 Enterprise 中,查询优化器会自动考虑索引视图。若要在其他所有版本中使用索引视图,则必须使用 NOEXPAND 表提示。
设计索引视图的指导原则
如果很少更新基础数据,则索引视图的效果最佳。维护索引视图的成本可能高于维护表索引的成本。如果经常更新基础数据,则维护索引视图数据的成本可能超过使用索引视图所带来的性能收益。如果基础数据以批处理的形式定期更新,但在更新之间主要作为只读数据进行处理,请考虑在更新前删除所有索引视图,然后再重新生成。这样做可以提高更新的性能。
索引视图可以提高下列查询类型的性能:
处理大量行的联接和聚合。
许多查询经常执行的联接和聚合操作。
例如,在记录库存的联机事务处理 (OLTP) 数据库中,许多查询都应联接 ProductMaster、ProductVendor 和 VendorMaster 表。虽然执行此联接的每个查询需要处理的行可能并不多,但若将成百上千个这样的查询联接起来,则总的处理量将非常大。因为这些关系不太可能经常更新,所以可以通过定义一个索引视图存储联接结果,以此提高整个系统的总体性能。
决策支持工作负荷。
分析系统的特点是存储不经常更新的、汇总的聚合数据。而许多决策支持查询的特点是进一步聚合数据并联接大量行。同时,决策支持系统有时包含具有大量列和/或较大列的宽表。引用这些列的窄子集的查询可以从只包含查询中的列或这些列的窄超集的索引视图中获益。创建包含单个表的列的子集的窄索引视图称为“垂直分区”策略,因为它垂直拆分表。例如,请考虑以下表和索引视图:
CREATE TABLE wide_tbl(a int PRIMARY KEY, b int, ..., z int) CREATE VIEW v_abc WITH SCHEMABINDING AS SELECT a, b, c FROM dbo.wide_tbl WHERE a BETWEEN 0 AND 1000 CREATE UNIQUE CLUSTERED INDEX i_abc ON v_abc(a)
只需使用 v_abc 即可应答以下查询:
SELECT b, count_big(*), SUM(c) FROM wide_tbl WHERE a BETWEEN 0 AND 1000 GROUP BY b
视图 v_abc 比表 wide_tbl 占的页少。因此,优化器选择通过它来解决上述查询可能会更好。
如果要垂直拆分整个表,而不是表的一个子集,建议您使用表的非聚集索引,该索引使用 INCLUDE 子句只包含所需的列,而不是索引视图。有关详细信息,请参阅 CREATE INDEX (Transact-SQL)。
索引视图通常不会提高下列查询类型的性能:
具有大量写操作的 OLTP 系统。
具有大量更新的数据库。
不涉及聚合或联接的查询。
GROUP BY 键具有高基数度的数据聚合。高基数度表示键包含许多不同的值。唯一键具有可能的最高基数度,因为每个键具有不同的值。索引视图通过减少查询必须访问的行数来提高性能。如果视图结果集中的行数像基表中的行数那么多,那么使用视图获得的性能收益微乎其微。例如,请考虑以下对一个包含 1,000 行的表的查询:
SELECT PriKey, SUM(SalesCol) FROM ExampleTable GROUP BY PriKey
如果表键的基数是 100,那么使用此查询结果生成的索引视图将只有 100 行。使用该视图的查询所需的平均读取量是需要对基表进行的读取量的十分之一。如果键是唯一键,则键的基数是 1000,而视图结果集将返回 1000 行。如果视图和 ExampleTable 基表的行数相等,那么查询使用此索引视图不会比直接读取基表获得更高的性能。
扩展联接。这些是结果集大于基表中的原始数据的视图。
索引视图与查询相结合
虽然对可索引的视图类型的限制可能使您无法设计完全解决某个问题的视图,但仍可以设计多个较小的索引视图,从一定程度上加快进程的速度。
请考虑下列示例:
假设有一个经常执行的查询首先要聚合一个数据库中的数据,然后聚合另一个数据库中的数据,再将结果联接起来。因为索引视图不能引用多个数据库中的表,所以不能设计单个视图来完成整个处理过程。但是,您可以在每个数据库中创建一个索引视图,分别为每个数据库执行聚合操作。如果优化器可以将这些索引视图与现有查询匹配,那么至少会加快聚合处理的速度,因为无须对现有查询重新编码。虽然联接处理速度没有加快,但因为查询使用了索引视图中存储的聚合,所以总体速度将加快。
假设有一个经常执行的查询首先要聚合几个表中的数据,然后使用 UNION 组合结果。索引视图中不允许使用 UNION。同样,您可以设计一些视图来执行每个聚合操作。这样一来,优化器就可以选择索引视图以加快查询的速度,而无须对查询重新编码。尽管 UNION 处理速度没有提高,但每个聚合进程的速度确实提高了。
设计可以满足多个操作的索引视图。因为即使未在 FROM 子句中指定,优化器也可以使用索引视图,所以,设计良好的索引视图可以加快许多查询的处理速度。
例如,请考虑对以下视图创建索引:
CREATE VIEW ExampleView WITH SCHEMABINDING
AS
SELECT GroupKey, SUM(Colx) AS SumColx, COUNT_BIG(Colx) AS CountColx
FROM MyTable
GROUP BY GroupKey
此视图不仅能满足直接引用视图列的查询,还可用于满足查询基表并包含 SUM(Colx)、COUNT_BIG(Colx)、COUNT(Colx) 和 AVG(Colx) 等表达式的查询。因为所有这些查询只须在视图中检索少量的行,而不需要从基表中读取全部行,所以查询速度将更快。
同样,聚合数据并按天分组的索引视图可用于满足聚合 1 天以上(如 7、30 或 90 天)的几个不同范围的查询。