实际执行计划中未使用索引视图?

Ala*_*ain 6 sql-server optimization materialized-view sql-server-2016

我有一个索引视图的适度用例,它折叠一列并总结一个大表中的所有条目:

CREATE VIEW dbo.Losses_CombinedPortfolio WITH SCHEMABINDING
AS
    SELECT [Category], [Year], 
           SUM(ISNULL(Loss,0)) AS [Loss], COUNT_BIG(*) as [Count]
    FROM dbo.Sub_Portfolio_Losses
    GROUP BY [Category], [Year]
GO
CREATE UNIQUE CLUSTERED INDEX Idx
ON dbo.Losses_CombinedPortfolio([Category], [Year]);
Run Code Online (Sandbox Code Playgroud)

我最初的目标更加雄心勃勃,但索引视图的限制如此之多......我很高兴能够让它发挥作用。

可悲的是,当我尝试对此索引视图进行基本查询时:

SELECT TOP (100) * 
FROM Losses_CombinedPortfolio 
ORDER BY Loss DESC
Run Code Online (Sandbox Code Playgroud)

...查询也很慢,实际执行计划表明它总是回到源表并每次从头开始计算聚合:

实际执行计划

我只能假设这是因为我计算出的“损失”列没有被具体化 - 但这会让我感到惊讶,因为聚集索引创建成功。


请注意,此视图的主要用例是按Loss降序排序,但我无法明确创建包含它的索引:

CREATE UNIQUE CLUSTERED INDEX Idx 
ON dbo.Losses_CombinedPortfolio
    (Category, Loss DESC, [Year]);
Run Code Online (Sandbox Code Playgroud)

我收到错误:

无法在视图 'dbo.Losses_CombinedPortfolio' 上创建索引或统计信息 'Idx',因为关键列 'Loss' 是不精确的、已计算的且未持久化。考虑删除对视图索引或统计键中的列的引用或更改列以使其精确。如果在基表中计算列,请考虑在那里将其标记为 PERSISTED。

我尝试通过将总损失转换为除float(甚至尝试将其截断为bigint)以外的类型来解决“不精确”,但似乎此错误源于用于计算总和的基础类型。

我很困惑 - 我看到其他问题声称他们能够成功地执行聚合,例如sum在他们的索引视图中,所以我不确定为什么这不起作用。

J.D*_*.D. 13

您需要使用NOEXPAND查询提示(我假设您使用的是 SQL Server 标准版),以便您的查询使用索引视图(而不是将其扩展到基础表)。这将解决您的第一个问题。

这是您在查询中使用它的方式:

SELECT TOP (100) * 
FROM Losses_CombinedPortfolio WITH (NOEXPAND)
ORDER BY Loss DESC
Run Code Online (Sandbox Code Playgroud)

您可以在Microsoft的查询提示文档中查看更多信息。但这是与您当前遇到的相关的部分:

仅当在查询的 SELECT 部分直接引用视图并且指定了 WITH (NOEXPAND) 或 WITH (NOEXPAND, INDEX( index_value [ ,...n ] ) ) 时,索引视图才不会展开。

这是您可以使用的少数查询提示之一,而不必担心它是不好的做法,实际上,出于某些原因也建议使用它。

至于用“不精确”错误您的问题,这是因为你猜对了,由于LossFLOAT类型的塔中(这是不准确的)。即使铸造它也无济于事;您必须先将基础类型更改为精确的类型,然后才能在视图中对其进行索引。(例如,如果您可以将数据类型更改为 DECIMAL,那么您就可以将该列添加到您的索引中。)

文档

任何浮点数实数表达式都被认为是不精确的,不能作为索引的键;一个浮动真实表达可以在索引视图但不作为密钥来使用。对于计算列也是如此。如果任何函数、表达式或用户定义的函数包含任何浮点实数表达式,则它被认为是不精确的。这包括逻辑的(比较)。