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

时间:2014-02-27 15:05:02

标签: sql-server sql-server-2012 indexed-view

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

有明显的表格:

CREATE TABLE Entity 
(
  ID int,
  Classificator1ID int,
  Classificator2ID int,
  Classificator3ID int,
  Classificator4ID int,
  Classificator5ID int
);

和视图:

CREATE VIEW dbo.EntityView (ID, Code1, Code2, Code3, Code4, Code5) 
WITH SCHEMABINDING

其中实体字段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);

但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%';

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

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

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

3 个答案:

答案 0 :(得分:4)

在2012 Developer Edition上运行时,未提示的查询的成本约为提示查询的8倍

enter image description here

虽然因数8可能听起来很多,但您的示例数据非常小,直接从基表中选择的成本为0.0267122 vs 0.003293,从视图中估算的成本。

Paul White在his answer here中解释说,如果首先找到足够低的计划,则甚至不会考虑自动索引视图匹配。

人为地提高所有相关表格的成本

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 

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

您现在应该看到匹配的视图(在Enterprise / Developer / Evaluation版本上),除非您另外明确提示。

SELECT * FROM EntityView;

SELECT * FROM EntityView OPTION (EXPAND VIEWS) 

enter image description here

答案 1 :(得分:4)

tl;博士回答:如果你没有指定NOEXPAND,查询优化器不知道你是从视图中提交一个简单的选择。它必须与您的查询(它看到的全部)的扩展匹配一些视图索引。当它与一群演员阵容进行五方联接时,可能不会打扰。

查看与查询匹配的索引是一个难题,我相信您的查看过于复杂,无法使查询引擎与索引匹配。考虑一下这个问题:

SELECT ID, Code1 FROM EntityView Where Code1 > 'NR%';

很明显,这可以使用视图索引,但这不是查询引擎看到的查询。如果您没有指定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;

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

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

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

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

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

SELECT ID, Code1 FROM EntityView Where Code1 > 'NR%';

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

(注意:你的SQL小提琴代码对同一个表有所有5个外键引用,这可能不是你想要的。)

答案 2 :(得分:1)

如果您使用的是SQL Server Enterprise

,请使用WITH(NOExpand)提示

您的查询将是SELECT * FROM EntityView with (noexpand)