根据条件重置的 7 天累积总和

Posted

技术标签:

【中文标题】根据条件重置的 7 天累积总和【英文标题】:7 day cumulative sum that resets on condition 【发布时间】:2018-12-21 15:57:44 【问题描述】:

我正在尝试编写一个脚本来计算客户在 7 天内每次花费超过 1200 欧元的时间。一旦客户超过 1200 欧元的门槛,累计金额应重置。例如,如果客户在第 3 天超过 1200 欧元,则该金额计为 1,累计金额应在第 4 天重置。

我见过类似的问题,其中包括重置累积总和。这些解决方案均不适用于 7 天滚动条件。

示例数据集

create table test2
(
  yyyymmdd   DATE not null,
  account_id NUMBER,
  vol_eur    NUMBER
);
insert into test2 (yyyymmdd, account_id, vol_eur)
values (to_date('21-01-2018 11:16:19', 'dd-mm-yyyy hh24:mi:ss'), 57642, 1500);
insert into test2 (yyyymmdd, account_id, vol_eur)
values (to_date('06-01-2018 09:51:23', 'dd-mm-yyyy hh24:mi:ss'), 57645, 190);
insert into test2 (yyyymmdd, account_id, vol_eur)
values (to_date('21-01-2018 07:09:35', 'dd-mm-yyyy hh24:mi:ss'), 57645, 300);
insert into test2 (yyyymmdd, account_id, vol_eur)
values (to_date('03-01-2018 14:58:14', 'dd-mm-yyyy hh24:mi:ss'), 57646, 1000);
insert into test2 (yyyymmdd, account_id, vol_eur)
values (to_date('17-01-2018 13:30:44', 'dd-mm-yyyy hh24:mi:ss'), 57646, 130);
insert into test2 (yyyymmdd, account_id, vol_eur)
values (to_date('03-01-2018 18:33:33', 'dd-mm-yyyy hh24:mi:ss'), 57647, 1000);
insert into test2 (yyyymmdd, account_id, vol_eur)
values (to_date('04-01-2018 08:44:33', 'dd-mm-yyyy hh24:mi:ss'), 57647, 270);
insert into test2 (yyyymmdd, account_id, vol_eur)
values (to_date('05-01-2018 19:28:08', 'dd-mm-yyyy hh24:mi:ss'), 57647, 800);
insert into test2 (yyyymmdd, account_id, vol_eur)
values (to_date('13-01-2018 12:24:21', 'dd-mm-yyyy hh24:mi:ss'), 57647, 700);
insert into test2 (yyyymmdd, account_id, vol_eur)
values (to_date('15-01-2018 10:52:50', 'dd-mm-yyyy hh24:mi:ss'), 57647, 1000);
insert into test2 (yyyymmdd, account_id, vol_eur)
values (to_date('27-01-2018 12:07:20', 'dd-mm-yyyy hh24:mi:ss'), 57647, 500);
insert into test2 (yyyymmdd, account_id, vol_eur)
values (to_date('10-01-2018 21:14:46', 'dd-mm-yyyy hh24:mi:ss'), 57647, 690);
insert into test2 (yyyymmdd, account_id, vol_eur)
values (to_date('30-01-2018 15:39:17', 'dd-mm-yyyy hh24:mi:ss'), 57647, 5500);
insert into test2 (yyyymmdd, account_id, vol_eur)
values (to_date('05-01-2018 19:43:38', 'dd-mm-yyyy hh24:mi:ss'), 57649, 300);
insert into test2 (yyyymmdd, account_id, vol_eur)
values (to_date('06-01-2018 17:54:30', 'dd-mm-yyyy hh24:mi:ss'), 57649, 150);
insert into test2 (yyyymmdd, account_id, vol_eur)
values (to_date('15-01-2018 19:38:36', 'dd-mm-yyyy hh24:mi:ss'), 57649, 1000);
insert into test2 (yyyymmdd, account_id, vol_eur)
values (to_date('20-01-2018 13:26:34', 'dd-mm-yyyy hh24:mi:ss'), 57649, 1150);
insert into test2 (yyyymmdd, account_id, vol_eur)
values (to_date('06-01-2018 17:09:54', 'dd-mm-yyyy hh24:mi:ss'), 57651, 300);
insert into test2 (yyyymmdd, account_id, vol_eur)
values (to_date('28-01-2018 17:31:14', 'dd-mm-yyyy hh24:mi:ss'), 57651, 250);
insert into test2 (yyyymmdd, account_id, vol_eur)
values (to_date('04-01-2018 13:39:06', 'dd-mm-yyyy hh24:mi:ss'), 57654, 150);
insert into test2 (yyyymmdd, account_id, vol_eur)
values (to_date('07-01-2018 13:18:26', 'dd-mm-yyyy hh24:mi:ss'), 57654, 200);
insert into test2 (yyyymmdd, account_id, vol_eur)
values (to_date('13-01-2018 19:44:08', 'dd-mm-yyyy hh24:mi:ss'), 57654, 150);
insert into test2 (yyyymmdd, account_id, vol_eur)
values (to_date('21-01-2018 16:18:05', 'dd-mm-yyyy hh24:mi:ss'), 57654, 150);
insert into test2 (yyyymmdd, account_id, vol_eur)
values (to_date('28-01-2018 10:53:03', 'dd-mm-yyyy hh24:mi:ss'), 57654, 60);
insert into test2 (yyyymmdd, account_id, vol_eur)
values (to_date('01-01-2018 12:09:00', 'dd-mm-yyyy hh24:mi:ss'), 57655, 1000);
insert into test2 (yyyymmdd, account_id, vol_eur)
values (to_date('01-01-2018 17:01:27', 'dd-mm-yyyy hh24:mi:ss'), 57655, 1000);
insert into test2 (yyyymmdd, account_id, vol_eur)
values (to_date('02-01-2018 19:30:31', 'dd-mm-yyyy hh24:mi:ss'), 57655, 200);
insert into test2 (yyyymmdd, account_id, vol_eur)
values (to_date('21-01-2018 15:52:29', 'dd-mm-yyyy hh24:mi:ss'), 57655, 1000);
insert into test2 (yyyymmdd, account_id, vol_eur)
values (to_date('21-01-2018 16:58:52', 'dd-mm-yyyy hh24:mi:ss'), 57655, 500);
insert into test2 (yyyymmdd, account_id, vol_eur)
values (to_date('11-01-2018 14:26:30', 'dd-mm-yyyy hh24:mi:ss'), 57661, 2000);
insert into test2 (yyyymmdd, account_id, vol_eur)
values (to_date('12-01-2018 21:54:25', 'dd-mm-yyyy hh24:mi:ss'), 57661, 500);
insert into test2 (yyyymmdd, account_id, vol_eur)
values (to_date('06-01-2018 16:46:25', 'dd-mm-yyyy hh24:mi:ss'), 57666, 5000);
insert into test2 (yyyymmdd, account_id, vol_eur)
values (to_date('10-01-2018 18:27:51', 'dd-mm-yyyy hh24:mi:ss'), 57666, 5000);
insert into test2 (yyyymmdd, account_id, vol_eur)
values (to_date('14-01-2018 18:52:14', 'dd-mm-yyyy hh24:mi:ss'), 57666, 5000);
insert into test2 (yyyymmdd, account_id, vol_eur)
values (to_date('20-01-2018 12:19:07', 'dd-mm-yyyy hh24:mi:ss'), 57666, 5000);
insert into test2 (yyyymmdd, account_id, vol_eur)
values (to_date('24-01-2018 18:38:40', 'dd-mm-yyyy hh24:mi:ss'), 57666, 2990);
insert into test2 (yyyymmdd, account_id, vol_eur)
values (to_date('30-01-2018 18:36:01', 'dd-mm-yyyy hh24:mi:ss'), 57666, 1980);
insert into test2 (yyyymmdd, account_id, vol_eur)
values (to_date('19-01-2018 18:48:44', 'dd-mm-yyyy hh24:mi:ss'), 57671, 2000);
insert into test2 (yyyymmdd, account_id, vol_eur)
values (to_date('19-01-2018 23:41:56', 'dd-mm-yyyy hh24:mi:ss'), 57671, 100);
insert into test2 (yyyymmdd, account_id, vol_eur)
values (to_date('21-01-2018 19:22:51', 'dd-mm-yyyy hh24:mi:ss'), 57671, 5000);
commit;

【问题讨论】:

这种类型的逻辑需要分层或递归查询。您使用的是什么版本的 Oracle? 谢谢。甲骨文 12c。 最初 7 天的开始日期应该是什么?显示预期的输出会有所帮助。 每位客户的第一次购买日期 您期望输出什么?特别是,如果连续 8 天的金额为 (200, 200, 200, 200, 200, 200, 100, 400),则从第 1 天到第 7 天的总和为 1100(因此在 7 天内不超过限制),而从第 2 天到第 8 天为 1400(因此超过了 7 天的限制)。 【参考方案1】:

一个带有递归 cte 的选项。

with rownums as (select t.*,row_number() over(partition by id order by dt) as rnum 
                 from tbl t
                )
,rsum as (select id,dt,val,rnum,val as cumsum,0 as dt_diff
          from rownums
          where rnum = 1
          union all
          select r.id
                ,r.dt
                ,r.val
                ,r.rnum
                ,case when dt_diff + rs.dt - r.dt > 7 then r.val
                      when dt_diff + rs.dt - r.dt <= 7 and r.val + rs.cumsum < 1200 then r.val+rs.cumsum
                 else 0 end
                ,case when dt_diff + rs.dt - r.dt > 7 then 0 
                 else dt_diff + rs.dt - r.dt end
          from rsum rs
          join rownums r on r.id = rs.id and r.rnum = rs.rnum+1
         ) 
select id,dt,val,case when cumsum = 0 and lag(cumsum,1) over(partition by id order by dt) <= 1200 then val+lag(cumsum,1) over(partition by id order by dt)
                      when cumsum = 0 and lag(cumsum,1) over(partition by id order by dt) > 1200 then val
                 else cumsum end as res
from rsum
order by 1,2
计算第一个 cte rownums 中每个用户 ID 的行数。 从之前定义的rownums cte 中选择第一行作为锚行,然后遍历其余行,与锚行连接,一次向前看一行。 case 表达式在这里检查条件。 rsum cte 将累积总和设置为0,这表示一个新组基于 7 天内超过 1200 的总和或一个新的 7 天周期开始。使用lag 最终计算这些行的值。

Sample Demo in SQL Server

【讨论】:

以上是关于根据条件重置的 7 天累积总和的主要内容,如果未能解决你的问题,请参考以下文章

Pyspark - 具有重置条件的累积和

如何根据spark scala中的条件进行累积和

Oracle SQL - 基于分组和条件运行求和

Pyspark - 获取具有条件的列的累积总和

基于条件的累积和,但条件结束后会重置

R具有条件和重置的累积和