【问题标题】:Is it possible to calculate the total sum within partition and cumulative sum between partitions?是否可以计算分区内的总和和分区之间的累积和?
【发布时间】:2022-01-20 22:50:05
【问题描述】:

我有兴趣根据组计算递减的累积总和,但是,我希望累积总和基于分组。换句话说,我希望一个组内的总和和组之间的累积和,group_id的顺序很重要。

例如,使用下面的示例数据:

group_id    Value
1           5
1           5
1           5
2           5
2           2
2           4
3           1
3           2
3           3

我正在尝试获得以下信息:

group_id    Value   CumSum
3           1       6
3           2       6
3           3       6
2           5       17
2           2       17
2           4       17
1           5       32
1           5       32
1           5       32

我尝试使用分区,然后对 group_id 进行排序,但它似乎不起作用,我们将不胜感激。

CREATE TABLE data (group_id INT, value INT)
insert into data values 
(1,5),
(1,5),
(1,5),
(2,5),
(2,2),
(2,4),
(3,1),
(3,2),
(3,3);

-- My Attempt:
select *, sum(value) over(partition by group_id order by group_id, value) as cum_sum
from data;

【问题讨论】:

  • 根据问题指南,请不要发布代码、数据、错误消息等的图像 - 将文本复制或输入到问题中。请保留将图像用于图表或演示渲染错误,无法通过文本准确描述的事情。
  • Teradata SQL Server - 为什么要同时标记这两个?
  • 在您的示例结果中,您首先放置了第 3 组,然后是第 2 组,然后是第 1 组。这种排序重要吗?特别是因为第 1 组和第 3 组只是 碰巧 有相同的总和。
  • @DaleK 我对 Teradata 很感兴趣,但是我添加了 sql server,因为我希望我可以将它翻译成 Teradata。此外,我将更新问题,使其不包含图像。谢谢哟
  • @AaronBertrand 最终结果的排序是故意的。

标签: sql sql-server tsql teradata


【解决方案1】:

这是可能的。
SUM OVER 中没有分区,只是按 group_id 排序。

select *
, [CumSum] = sum(value) over(order by group_id desc)
from data
order by group_id desc;
group_id value CumSum
3 1 6
3 2 6
3 3 6
2 5 17
2 2 17
2 4 17
1 2 23
1 3 23
1 1 23

测试 dbfiddle here

【讨论】:

  • 我在路上有something similar,但顺序不清楚。
  • 一定很接近。好吧,atm 不确定,因为他的示例 sql 没有下降。
  • 对,第一个屏幕截图显示 1,2,3,然后所需的结果显示 3,2,1。 (但我不知道这是否是故意的......我只是提到这就是我放弃的原因。):-)
【解决方案2】:

在 SQL Server 中,您可以使用 row_number() 查找每个 group_id 中的第一行,并为该组求和 value。然后在 CTE 的帮助下建立一个累积和。

with cte as (
    select *
        , sum([value]) over (partition by group_id) cum_sum
        , row_number() over (partition by group_id order by group_id) group_row
    from #data
)
select group_id, [value]
    , sum(case when group_row = 1 then cum_sum else 0 end) over (order by group_id) cum_sum
from cte
order by group_id;

【讨论】:

  • SUM() 有一个古怪的行为 - 至少对我来说,试图为它找到一些真正的文档,因为 it doesn't seem to be mentioned here - 其中OVER 没有PARTITION BY 会像DISTINCT而不是ALL 针对ORDER BY 中引用的列。因此,它为group_id 返回相同的SUM,而不会过度计数,也不会复杂地为每组挑选一行。 (+1 因为我认为这仍然是正确的答案,这取决于易于修复的顺序......
  • @AaronBertrand 您将RANGE UNBOUNDED PRECEDING 视为默认设置,而不是大多数人所期望的ROWS UNBOUNDED PRECEDING (fully documented),这会让很多人绊倒。但是,在没有ORDER BY 的情况下,这无关紧要,因此它仅适用于此处的第二个SUM
  • @Charlieface 您是否看过其他答案(没有PARTITION BY 会产生正确的结果)?这就是我要说的那个。本能地你会认为分区是需要的,因为它不是一个简单的运行总计。
  • @AaronBertrand 不,我没有正确看待它,是的,我认为这是我见过的唯一一种实际需要 RANGE 的情况
【解决方案3】:

@LukStorms 解决方案在 Teradata 中无法按预期工作,因为它不支持 RANGE,因此当存在 ORDER BY 时,标准 SQL 不支持默认的 RANGE UNBOUNDED PRECEDING(它默认为 GROUP Sum)。

与 Dale K 的回答不同的逻辑:

WITH cte AS
 (
   SELECT group_id, value
      -- cumulative sum per group
     ,SUM(value) OVER (ORDER BY group_id DESC ROWS UNBOUNDED PRECEDING) AS cumsum
   FROM data
 )
SELECT group_id, value
   -- max per group
  ,MAX(cumsum) OVER (PARTITION BY group_id)
FROM cte;

【讨论】:

    猜你喜欢
    • 2023-03-16
    • 2021-06-01
    • 2019-01-09
    • 2015-07-20
    • 2020-12-23
    • 1970-01-01
    • 2020-08-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多