带复位值的累积和然后恢复

Posted

技术标签:

【中文标题】带复位值的累积和然后恢复【英文标题】:cummulative sum with reset value and then resum 【发布时间】:2017-11-01 22:55:48 【问题描述】:

我有以下数据:

lbid | lbdate     | lbtype        | lbamount
-----+------------+---------------+---------
   1 | 2017-11-01 | Add Plafon    |     20
   2 | 2017-11-02 | Use Balance   |      5
   3 | 2017-11-03 | Add Balance   |      1
   4 | 2017-11-04 | Reduce Plafon |     10
   5 | 2017-11-06 | Use Balance   |      8
   6 | 2017-11-07 | Add Balance   |      2
   7 | 2017-11-08 | Reduce Plafon |      5
   8 | 2017-11-10 | Add Plafon    |     10
   9 | 2017-11-11 | Use Balance   |      1
  10 | 2017-11-12 | Reduce Plafon |      5

基本上我期望的最终结果是这样的:

lbid | lbdate     | lbtype        | lbamount  | sumplafon | sumbalance
-----+------------+---------------+-----------+-----------+-------------
   1 | 2017-11-01 | Add Plafon    |     20    |       20  |        20
   2 | 2017-11-02 | Use Balance   |      5    |       20  |        15
   3 | 2017-11-03 | Add Balance   |      1    |       20  |        16
   4 | 2017-11-04 | Reduce Plafon |     10    |       10  |        10
   5 | 2017-11-06 | Use Balance   |      8    |       10  |         2
   6 | 2017-11-07 | Add Balance   |      2    |       10  |         4
   7 | 2017-11-08 | Reduce Plafon |      5    |        5  |         4
   8 | 2017-11-10 | Add Plafon    |     10    |       15  |        14
   9 | 2017-11-11 | Use Balance   |      1    |       15  |        15
  10 | 2017-11-12 | Reduce Plafon |      5    |       10  |        10

sumplafon 是所有 lbamount 的总和,其中 lbtype 是 Add Balance(正)和 Reduce Balance(负,减去 sumplafon)。

我已经这样做了。

sum(
    case
        when "lbtype" = 'Add Plafon' then "lbamount"
        when "lbtype" = 'Reduce Plafon' then -1 * "lbamount"
        else 0
    end
) over (order by "lbdate") sumplafon

而 sumbalance 是所有 lbamount 的总和,其中 lbtype 是 Add Plafon(正)、Use Balance(正)、Use Balance(负),但是每次找到 lbtype Reduce Plafon 时,sumbalance 将重置为 sumplafon,如果sumbalance 大于 sumplafon。

例如lbtype为Reduce Plafon的lbid 4,sumbalance为16且大于sumplafon 10,因此sumbalance需要重置为其sumplafon为10,然后再继续sumbalance的累计和。

我尝试通过先在 cte 中使用这样的计数来准备组。

count(
   case when "lbtype" = 'Reduce Plafon' then 1 else null end
) over (order by "lbdate") countplafon

然后在第二个 cte 中,我通过在第一个 cte 中使用 countplafon 的分区来求和,如下所示:

sum(
    case
        when "lbtype" = 'Add Plafon' or "lbtype" = 'Add Balance' then "lbamount"
        when "lbtype" = 'Use Balance' then -1 * "lbamount"
        else 0
    end
) over (partition by "countplafon" order by "lbdate") sumbalance

但结果只是从头开始重置 sumbalance,因为它使用 group by countplafon。

lbid | lbdate     | lbtype        | lbamount  | countplafon |sumplafon  | sumbalance
-----+------------+---------------+-----------+-----------+-------------|-----------
   1 | 2017-11-01 | Add Plafon    |     20    |           0 |       20  |        20
   2 | 2017-11-02 | Use Balance   |      5    |           0 |       20  |        15
   3 | 2017-11-03 | Add Balance   |      1    |           0 |       20  |        16
   4 | 2017-11-04 | Reduce Plafon |     10    |           1 |       20  |         0
   5 | 2017-11-06 | Use Balance   |      8    |           1 |       20  |        -8
   6 | 2017-11-07 | Add Balance   |      2    |           1 |       20  |        -6
   7 | 2017-11-08 | Reduce Plafon |      5    |           2 |       20  |         0
   8 | 2017-11-10 | Add Plafon    |     10    |           2 |       20  |        10
   9 | 2017-11-11 | Use Balance   |      1    |           2 |       20  |         9
  10 | 2017-11-12 | Reduce Plafon |      5    |           3 |       20  |         0

这里是sqlfiddle。

这里是sql。

with
    cte_runningnumbers1
    as (
        select
            "lbid",
            "lbdate",
            "lbtype",
            "lbamount",
            count(
                case when "lbtype" = 'Reduce Plafon' then 1 else null end
            ) over (order by "lbdate") countplafon,
            sum(
                case
                    when "lbtype" = 'Add Plafon' then "lbamount"
                    when "lbtype" = 'Reduce Plafon' then -1 * "lbamount"
                    else 0
                end
            ) over (order by "lbdate") sumplafon
        from    "lb"
    ),
    cte_runningnumbers2 as (
        select
            *,
            sum(
                case
                    when "lbtype" = 'Add Plafon' or "lbtype" = 'Add Balance' then "lbamount"
                    when "lbtype" = 'Use Balance' then -1 * "lbamount"
                    else 0
                end
            ) over (partition by "countplafon" order by "lbdate") sumbalance
        from    "cte_runningnumbers1"
    )
select  *
from    cte_runningnumbers2

我一直在关注这个SO question,但我仍然对如何解决我的问题感到困惑。

我需要做的最后一步是将它与之前的 sumbalance 或 sumplafon 相加(如果 sumbalance 大于 sumplafon),但我不知道该怎么做。谁能帮帮我?

【问题讨论】:

【参考方案1】:

创建一个custom aggregate function. 将逻辑放在一个状态转换函数中:

create or replace function lb_agg_fun(sumbalance numeric, lbtype text, lbamount numeric)
returns numeric language sql as $$
    select case
        when lbtype in ('Add Plafon', 'Add Balance') then sumbalance + lbamount
        when lbtype = 'Use Balance' then sumbalance - lbamount
        else case
            when lbamount < sumbalance then lbamount
            else sumbalance
        end
    end;
$$;

Create an aggregate:

create aggregate lb_agg(text, numeric) (
    sfunc = lb_agg_fun,
    stype = numeric,
    initcond = 0
);

并使用它:

select *, lb_agg(lbtype, lbamount) over (order by lbdate) as sumbalance
from lb;

 lbid |   lbdate   |    lbtype     | lbamount | sumbalance 
------+------------+---------------+----------+------------
    1 | 2017-11-01 | Add Plafon    |       20 |         20
    2 | 2017-11-02 | Use Balance   |        5 |         15
    3 | 2017-11-03 | Add Balance   |        1 |         16
    4 | 2017-11-04 | Reduce Plafon |       10 |         10
    5 | 2017-11-06 | Use Balance   |        8 |          2
    6 | 2017-11-07 | Add Balance   |        2 |          4
    7 | 2017-11-08 | Reduce Plafon |        5 |          4
    8 | 2017-11-10 | Add Plafon    |       10 |         14
    9 | 2017-11-11 | Use Balance   |        1 |         13
   10 | 2017-11-12 | Reduce Plafon |        5 |          5
(10 rows)

【讨论】:

以上是关于带复位值的累积和然后恢复的主要内容,如果未能解决你的问题,请参考以下文章

结构体状态复位函数/使用完后将结构体恢复成初始值的怎么写呢?

海康威视摄像头被其它人控制必须复位吗

arduino恢复出厂设置

启动 停止 复位使用状态机

总结“异步复位,同步释放”

R(dplyr)中复位的条件运行计数(累计和)