SQL Server 索引视图:无法创建聚集索引,因为选择列表包含聚合函数结果的表达式

Posted

技术标签:

【中文标题】SQL Server 索引视图:无法创建聚集索引,因为选择列表包含聚合函数结果的表达式【英文标题】:SQL Server Indexed Views: Cannot create clustered index because the select list contains an expression on result of aggregate function 【发布时间】:2015-05-27 05:25:50 【问题描述】:

我正在尝试为下面的查询创建一个简单的索引视图。但是,当我尝试在其上创建唯一聚集索引时,出现以下错误:

无法在视图“..”上创建聚集索引“..”,因为选择 视图列表包含聚合结果的表达式 功能或分组列。考虑删除结果表达式 选择列表中的聚合函数或分组列。

我使用的查询如下:

SELECT 
    [Manufacturer]
    ,ISNULL(SUM([QAV]),0) as AvgQAV
    ,ISNULL(SUM([BackOrders$]),0)as AvgBackorder$
    ,DATEPART(year,[Date])as Year
    ,DATEPART(month,[Date])as Month
    ,[fixSBU]
    ,[DC Name]
FROM [dbo].[TABLE1]
Group By
    [Manufacturer]      
    ,DATEPART(year,[Date])
    ,DATEPART(month,[Date])
    ,[fixSBU]
    ,[DC Name]

谁能告诉我这可能的原因? 如您所见,我已经在使用ISNULL 函数。

【问题讨论】:

您不能基于聚合数据(即 SUM(BackOrders$))创建索引,因为每次修改基础表时,索引都必须重新分组/计算然后更新。您可以使用您正在使用的列(fixSBU 和 [DC 名称])在基础评级表上创建索引,如果表很大,这应该会有所帮助,否则会忘记索引。 @mohan111 尽管基础数据可能会发生变化,但 sum 仍然是一个确定性函数 - 因为每次提供相同的基础数据时它都会返回相同的结果。我认为这应该仍然有效(至少由于总和) 只是为了澄清你 100% 可以根据聚合数据创建索引。您必须小心 - SELECT SUM(Total) - SUM(Discount) AS SubTotal 是不允许的,但 SELECT SUM(Total - Discount) AS SubTotal 是。 【参考方案1】:

这里是索引视图所有限制的链接:https://msdn.microsoft.com/en-us/library/ms191432.aspx#Restrictions

从文档中这两项应该突出:

如果存在 GROUP BY,则 VIEW 定义必须包含 COUNT_BIG(*) 并且不得包含 HAVING。这些 GROUP BY 限制是 仅适用于索引视图定义。查询可以使用 其执行计划中的索引视图,即使它不满足这些 GROUP BY 限制。 如果视图定义包含 GROUP BY 子句,唯一聚集索引的键只能引用 GROUP BY 子句中指定的列。

另外,您需要更改您的 ISNULL 语句。现在你有 ISNULL(SUM([BackOrders$]),0),它应该是 SUM(ISNULL([BackOrders$], 0))。您需要对 ISNULL 求和,而不是相反。

【讨论】:

我也试过在 SELECT 语句中使用类似的东西 - ISNULL(SUM([QAV]),0)/COUNT_BIG(*) as AvgQAV。那也行不通。我也会尝试你建议的 ISNULL 修改。谢谢! 我更改了 ISNULL 语句并在查询中引入了另一个 COUNT_BIG(),但我仍然得到同样的错误。 ` SELECT [制造商] ,SUM(ISNULL([QAV],0))/COUNT_BIG() as AvgQAV ,SUM(ISNULL([BackOrders$],0))/COUNT_BIG()as AvgBackorder$ ,DATEPART(year,[Date])as Year ,DATEPART(month,[Date])as Month ,[fixSBU] ,[DC Name] ,COUNT_BIG() AS NumRows FROM [Table1] Group By [Manufacturer] ,DATEPART(year,[Date]) ,DATEPART(month,[Date]) ,[fixSBU] ,[DC Name]` 我去掉了上面查询 (SUM(ISNULL([BackOrders$],0))/COUNT_BIG(*)) 中的表达式,改变了 ISNULL 并创建了一个单独的 COUNT_BIG(*) -- 成功了!非常感谢!【参考方案2】:

没有多大意义(至少对我来说不是)但参考:https://msdn.microsoft.com/en-us/library/ms191432.aspx

具体来说:

如果存在 GROUP BY,则 VIEW 定义必须包含 COUNT_BIG(*) 且不得包含 HAVING。这些 GROUP BY 限制仅适用于索引视图定义。查询可以在其执行计划中使用索引视图,即使它不满足这些 GROUP BY 限制。

尝试将COUNT_BIG(*) 添加到您的选择列表中并试一试。

【讨论】:

我也尝试过使用 COUNT_BIG(*)。那也行不通。一旦我从 SELECT 中删除 sum 函数,我就能够创建唯一的聚集索引,但聚合是这里的关键。 SELECT [Manufacturer] ,ISNULL(SUM([QAV]),0)/COUNT_BIG(*) as AvgQAV ,ISNULL(SUM([BackOrders$]),0)/COUNT_BIG(*)as AvgBackorder$ ,DATEPART(year,[Date])as Year ,DATEPART(month,[Date])as Month ,[fixSBU] ,[DC Name] FROM [Table1] Group By [Manufacturer] ,DATEPART(year,[Date]) ,DATEPART(month,[Date]) ,[fixSBU] ,[DC Name] @user2673722:对我来说似乎很简单。您不能在索引视图中使用聚合函数。它在文档中,错误消息很清楚。我们还能做什么? @siride 您可以在索引视图中使用聚合。其中一个必须是 COUNT_BIG 并且他需要对 ISNULL 求和,而不是对 ISNULL 求和。 这是什么版本的sql server?根据technet.microsoft.com/en-us/library/cc917715.aspx,从 sql server 2005 开始 sum 可以在索引视图中使用,您运行的是 2000 还是更早版本? @zgirod:是的。这是一个有用的例外。然后轻松修复。【参考方案3】:

我遇到了类似的问题。我的一个选择字段如下所示:

sum(Pa * (CTRatio1a/CTRatio2a) * (VTRatio1/VTRatio2)* Polarity * [Percentage])/1000.0

通过在括号中包含最后一个除以 1000,它解决了问题:

sum(Pa * (CTRatio1a/CTRatio2a) * (VTRatio1/VTRatio2)* Polarity * [Percentage]/1000.0)

【讨论】:

【参考方案4】:

提示:数据库中最好有一个真实的日期字段,而不仅仅是年/月。这样,除了聚集索引之外,您还可以创建日期索引。

但是,如果您有 FullDateYearMonth,您会收到相同的错误消息 view contains an expression on result of aggregate function or grouping column

如果您这样做,可能会发生该错误:

SELECT 

    [Manufacturer]  
    ,[Date] as FullDate
    ,DATEPART(year,[Date]) as Year
    ,DATEPART(month,[Date]) as Month

    ,COUNT_BIG(*) as Count
    ,SUM(OrderValue) as TotalOrderValue

FROM [dbo].[TABLE1]
Group By

    [Manufacturer]      

    ,[Date]
    ,DATEPART(year,[Date])
    ,DATEPART(month,[Date])

虽然不是很明显发生了什么,但我认为这是因为它查看了分组列中的Date 并找到了其他列中使用的Date(针对年份和月份)。显然,尽管这在逻辑上应该可行,并且您应该能够像那样进行分组。

我发现了一个让它起作用的技巧:

SELECT 

    [Manufacturer]  
    ,DATEADD(day, 0, [Date]) as FullDate
    ,DATEPART(year,[Date])as Year
    ,DATEPART(month,[Date])as Month

    ,COUNT_BIG(*) as Count
    ,SUM(OrderValue) as TotalOrderValue

FROM [dbo].[TABLE1]
Group By

    [Manufacturer]      

    ,DATEADD(day, 0, [Date])
    ,DATEPART(year,[Date])
    ,DATEPART(month,[Date])

这诱使解析器允许它,现在我可以创建一个单独的索引(在聚集后)以按 FullDate 搜索。

奖励:我偶然发现这个的真正原因是因为我需要计算成本高昂的 ISO_WEEK 和 ISO_YEAR。这是我为此使用的分组子句的最终完整列表:

-- date
DATEADD(day, 0, [Order].OrderDateDt) as OrderDateDt, -- trick! add 0 days to date 

-- day / month / year / quarter
DATEPART(day, [Order].OrderDateDt) as OrderDate_day,
DATEPART(month, [Order].OrderDateDt) as OrderDate_month,
DATEPART(year, [Order].OrderDateDt) as OrderDate_year,
DATEPART(quarter, [Order].OrderDateDt) as OrderDate_quarter,

-- iso week
DATEPART(iso_week, [Order].OrderDateDt) as OrderDate_isoweek,
YEAR(DATEADD(day, 26 - DATEPART(iso_week, [Order].OrderDateDt), [Order].OrderDateDt)) as OrderDate_isoyear

确保在 SELECT 和 GROUP BY 中包含完全相同的所有这些内容。

【讨论】:

注意:这可能会增加更新索引的复杂性 - 因此请务必检查您获得的查询计划以进行插入和更新。

以上是关于SQL Server 索引视图:无法创建聚集索引,因为选择列表包含聚合函数结果的表达式的主要内容,如果未能解决你的问题,请参考以下文章

SQL Server查询优化和事务处理

SQL Server 索引(index) 和 视图(view) 的简单介绍和操作

SQL Server 索引视图

Sql server 索引视图

SQL Server索引的创建与维护

SQL Server索引的创建与维护