为什么Query Optimizer完全忽略索引视图索引?

Rom*_*kij 10 sql-server indexed-view sql-server-2012

SQL小提琴:http://sqlfiddle.com/#!6/d4496/1 (为您的实验预先生成数据)

有明显的表:

CREATE TABLE Entity 
(
  ID int,
  Classificator1ID int,
  Classificator2ID int,
  Classificator3ID int,
  Classificator4ID int,
  Classificator5ID int
);
Run Code Online (Sandbox Code Playgroud)

和观点:

CREATE VIEW dbo.EntityView (ID, Code1, Code2, Code3, Code4, Code5) 
WITH SCHEMABINDING
Run Code Online (Sandbox Code Playgroud)

其中实体字段Classificator1ID..Classificator5ID已解析为分类器值Code1..Code5

并且此视图上有很多索引:

CREATE UNIQUE CLUSTERED INDEX [IXUC_EntityView$ID] ON EntityView
  ([ID]);
CREATE UNIQUE NONCLUSTERED  INDEX [IXU_EntityView$ID$include$ALL] ON EntityView
  ([ID]) INCLUDE (Code1, Code2, Code3, Code4,  Code5);
CREATE UNIQUE NONCLUSTERED  INDEX [IXU_EntityView$ALL] ON EntityView
  ([ID],Code1, Code2, Code3, Code4,  Code5);  
CREATE UNIQUE NONCLUSTERED  INDEX [IXU_EntityView$ID$Code1] ON EntityView
  ([ID],Code1);
CREATE UNIQUE NONCLUSTERED  INDEX [IXU_EntityView$ID$include$Code1] ON EntityView
  ([ID])INCLUDE (Code1);
CREATE NONCLUSTERED  INDEX [IX_EntityView$Code1] ON EntityView
  (Code1);
CREATE NONCLUSTERED  INDEX [IX_EntityView$Code1$include$ID] ON EntityView
  (Code1) INCLUDE (ID);
Run Code Online (Sandbox Code Playgroud)

但QO从不使用它们!试试这个:

SELECT * FROM EntityView;

SELECT ID, Code1 FROM EntityView;

SELECT ID, Code1, Code2, Code3, Code4, Code5 FROM EntityView;

SELECT ID, Code1, Code2, Code3, Code4, Code5 FROM EntityView WHERE ID=1;

SELECT ID, Code1 FROM EntityView Where Code1 like 'NR%';
Run Code Online (Sandbox Code Playgroud)

为什么?尤其是"包含"索引有什么问题?创建索引,包含所有字段但仍未使用...

补充:这只是测试!请不要这么生气,不要逼我去分析那些指标的致瘾问题.

在我的真实项目中,我无法解释为什么QO会忽略索引视图(非常非常有用的索引视图).但有时我看到它在其他地方使用它们.我创建了这个db片段来试验索引公式,但可能我应该做更多的事情:以某种方式调整statistcs?

Ste*_*ass 5

tl;dr 回答:如果您不指定 NOEXPAND,查询优化器不知道您正在提交一个简单的视图选择。它必须将查询的扩展(这是它看到的全部内容)与某些视图索引相匹配。当它是一个带有一堆演员表的五向连接时,可能不会打扰。

查看与查询的索引匹配是一个难题,我相信您的视图对于查询引擎来说太复杂了,无法与索引匹配。考虑一下您的查询之一:

SELECT ID, Code1 FROM EntityView Where Code1 > 'NR%';
Run Code Online (Sandbox Code Playgroud)

很明显,这可以使用视图索引,但这不是查询引擎看到的查询。如果您不指定 NOEXPAND,视图会自动展开,所以这就是查询引擎的内容:

SELECT ID, Code1 FROM (
    SELECT e.ID, 'NR'+CAST(c1.CODE as nvarchar(11)) as Code1, 'NR'+CAST(c2.CODE as nvarchar(11)) as Code2, 'NR'+CAST(c3.CODE as nvarchar(11)) as Code3, 'NR'+CAST(c4.CODE as nvarchar(11)) as Code4, 'NR'+CAST(c5.CODE as nvarchar(11)) as Code5
    FROM dbo.Entity e
        inner join  dbo.Classificator1 c1 on e.ID = c1.ID
        inner join  dbo.Classificator2 c2 on e.ID = c2.ID
        inner join  dbo.Classificator3 c3 on e.ID = c3.ID
        inner join  dbo.Classificator4 c4 on e.ID = c4.ID
        inner join  dbo.Classificator5 c5 on e.ID = c5.ID;
) AS V;
Run Code Online (Sandbox Code Playgroud)

查询引擎看到这个复杂的查询,它有描述已定义视图索引的信息(但可能不是视图定义的 SQL)。鉴于此查询和视图索引都有多个连接和强制转换,匹配是一项艰巨的工作。

请记住,您知道此查询和视图索引中的连接和匹配是相同的,但查询处理器不知道这一点。它处理这个查询就像它加入了 Classificator3 的五个副本,或者如果其中一列是 'NQ'+CAST(c2.CODE as varchar(12))。视图索引匹配器(假设它试图匹配这个复杂的查询)必须将此查询的每个细节与所涉及的表上的视图索引的细节相匹配。

查询引擎的第一目标是找出一种有效执行查询的方法。它可能不是为了花大量时间尝试将五向连接和 CAST 的每个细节与视图索引匹配而设计的。

如果我不得不猜测,我怀疑视图索引匹配器看到查询的结果列甚至不是任何基础表的列(因为 CAST)并且根本不费心去尝试任何事情。补充我错了。我刚刚尝试了 Martin 的更新统计信息的建议,以使查询变得昂贵,并且在没有 NOEXPAND 的情况下,为其中一些查询匹配了视图索引。视图匹配器比我想象的更聪明!所以问题是如果成本非常高,视图匹配器可能会更努力地匹配复杂的查询。

使用 NOEXPAND 提示,而不是期望查询引擎能够找出此处匹配的内容。NOEXPAND 绝对是你的朋友,因为这样查询引擎就会看到

SELECT ID, Code1 FROM EntityView Where Code1 > 'NR%';
Run Code Online (Sandbox Code Playgroud)

然后对于视图索引匹配器来说很明显有一个有用的索引。

(注意:您的 SQL Fiddle 代码包含对同一个表的所有 5 个外键引用,这可能不是您想要的。)


Mar*_*ith 4

在 2012 Developer Edition 上运行,无提示查询的成本大约是提示查询的 8 倍

在此输入图像描述

虽然 8 的系数听起来可能很大,但您的示例数据非常小,并且直接从基表中选择的成本与0.02671220.003293视图中估计的成本相当。

保罗·怀特 (Paul White) 解释道他的回答中解释说,如果首先找到足够低的计划,甚至不会考虑自动索引视图匹配。

人为地提高所有涉及表的成本

UPDATE STATISTICS Classificator1 WITH ROWCOUNT = 60000000, PAGECOUNT = 10000000 
UPDATE STATISTICS Classificator2 WITH ROWCOUNT = 60000000, PAGECOUNT = 10000000 
UPDATE STATISTICS Classificator3 WITH ROWCOUNT = 60000000, PAGECOUNT = 10000000 
UPDATE STATISTICS Classificator4 WITH ROWCOUNT = 60000000, PAGECOUNT = 10000000 
UPDATE STATISTICS Classificator5 WITH ROWCOUNT = 60000000, PAGECOUNT = 10000000 
UPDATE STATISTICS Entity         WITH ROWCOUNT = 60000000, PAGECOUNT = 10000000 
Run Code Online (Sandbox Code Playgroud)

将基表计划的成本增加到 29122.6

您现在应该看到视图正在匹配(在企业版/开发版/评估版上),除非您明确提示否则。

SELECT * FROM EntityView;

SELECT * FROM EntityView OPTION (EXPAND VIEWS) 
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述