SQL 聚合 OVER 和 PARTITION

Posted

技术标签:

【中文标题】SQL 聚合 OVER 和 PARTITION【英文标题】:SQL Aggregates OVER and PARTITION 【发布时间】:2014-08-29 16:23:13 【问题描述】:

全部,

这是我在 *** 上的第一篇文章,所以放轻松...

我使用的是 SQL Server 2008。

我对编写 SQL 查询相当陌生,我遇到了一个我认为很简单的问题,但我已经奋斗了 2 天。我有一组如下所示的数据:

UserId          Duration(Seconds)        Month
1               45                       January
1               90                       January
1               50                       February
1               42                       February
2               80                       January
2               110                      February
3               45                       January
3               62                       January
3               56                       January
3               60                       February

现在,我想要编写一个查询,为我提供特定用户的平均值,并将其与该月所有用户的平均值进行比较。因此,查询用户 #1 后的结果数据集如下所示:

UserId         Duration(seconds)        OrganizationDuration(Seconds)        Month
1              67.5                     63                                   January
1              46                       65.5                                 February

我一直在尝试不同的子查询并按场景分组,但似乎没有任何效果。最近,我一直在尝试 OVER 和 PARTITION BY,但也没有成功。我的最新查询如下所示:

select Userid, 
       AVG(duration) OVER () as OrgAverage,
       AVG(duration) as UserAverage,
       DATENAME(mm,MONTH(StartDate)) as Month
            from table.name 
            where YEAR(StartDate)=2014
            AND userid=119 
                  GROUP BY MONTH(StartDate), UserId     

此查询以“选择列表中的“持续时间”无效,因为它既不包含在聚合函数或 GROUP BY 子句中”错误而被炸毁。

请记住,我正在处理大量数据。我想我可以让它与 CASE 语句一起使用,但如果可能的话,我正在寻找一种更简洁、更有效的方式来编写查询。

谢谢!

【问题讨论】:

我认为 OrgAverage 需要“OVER (Partition by Month(StartDate))”,UserAverage 需要“OVER (Partition By Month(StartDate),UserId)”。然后摆脱 Group By。如果这不起作用,您可能必须使用子查询或派生表来获取 OrgAverage。 这是个好主意。我实际上认为这会起作用,但不幸的是,结果集一遍又一遍地显示相同的 OrgAverage 和 UserAverage,以及同一个月(一月)一遍又一遍。 【参考方案1】:

您在这里将两个查询连接在一起:

每个用户每月的平均值 每月所有组织的平均值

如果您一次只为一个用户返回数据,那么内联选择可能会给您带来乐趣:

SELECT AVG(a.duration) AS UserAvergage,
   (SELECT AVG(b.Duration) FROM tbl b WHERE MONTH(b.StartDate) = MONTH(a.StartDate)) AS OrgAverage 
    ...
    FROM tbl a
    WHERE userid = 119 
    GROUP BY MONTH(StartDate), UserId

注意 - 在 MONTH 上使用比较可能会很慢 - 你最好使用 CTE(通用表表达式)

【讨论】:

【参考方案2】:

Average 函数中缺少分区子句

OVER ( Partition by MONTH(StartDate)) 

【讨论】:

【参考方案3】:
Please try this. It works fine to me.

WITH C1
AS
(
SELECT 
AVG(Duration) AS TotalAvg, 
[Month]
FROM [dbo].[Test]
GROUP BY [Month]
),
C2
AS
(
SELECT Distinct UserID,
AVG(Duration) OVER(PARTITION BY UserID, [Month] ORDER BY UserID) AS DetailedAvg, 
[Month]
FROM [dbo].[Test]
)
SELECT C2.*, C1.TotalAvg
FROM C2 c2 
INNER JOIN C1 c1 ON c1.[Month] = c2.[Month]
ORDER BY c2.UserID, c2.[Month] desc;

【讨论】:

【参考方案4】:

我能够使用自联接完成它,可能有更好的方法。

Select UserId, AVG(t1.Duration) as Duration, t2.duration as OrgDur, t1.Month 
from #temp t1
inner join (Select Distinct MONTH, AVG(Duration) over (partition by Month) as duration
from #temp) t2 on t2.Month = t1.Month
group by t1.Month, t1.UserId, t2.Duration 
order by t1.UserId, Month desc

这里使用 CTE,这可能是一个更好的解决方案,而且绝对更容易阅读

With MonthlyAverage
as 
(
Select MONTH, AVG(Duration) as OrgDur 
from #temp
group by Month
)

Select UserId, AVG(t1.Duration) as Duration, m.duration as OrgDur , t1.Month 
from #temp t1
inner join MonthlyAverage m on m.Month = t1.Month
group by UserId, t1.Month, m.duration

【讨论】:

【参考方案5】:

您可以在下面尝试使用更少的代码。

SELECT Distinct UserID,
AVG(Duration)  OVER(PARTITION BY [Month]) AS TotalAvg,
AVG(Duration) OVER(PARTITION BY UserID, [Month] ORDER BY UserID) AS DetailedAvg, 
[Month]
FROM [dbo].[Test]

【讨论】:

这个炸弹告诉我'order'附近的语法不正确。分区中的“排序依据”是否在 2008 年有效?我知道窗口功能在 2012 年和 2014 年得到了一些升级。 您可以尝试删除订单...没有问题

以上是关于SQL 聚合 OVER 和 PARTITION的主要内容,如果未能解决你的问题,请参考以下文章

sql中的 开窗函数over() 聚合函数 排名函数

SQL Server:使用具有相同 OVER 子句的多个聚合/分析函数?

开窗函数

OVER 子句中的 SQL ORDER BY 与 CLR 聚合不兼容?

oracle中的over函数怎么用的,啥意思

SQL Server2008窗口计算