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】:提示:数据库中最好有一个真实的日期字段,而不仅仅是年/月。这样,除了聚集索引之外,您还可以创建日期索引。
但是,如果您有 FullDate
、Year
和 Month
,您会收到相同的错误消息 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 索引视图:无法创建聚集索引,因为选择列表包含聚合函数结果的表达式的主要内容,如果未能解决你的问题,请参考以下文章