Oracle SQL 递归加值

Oracle SQL recursive adding values

我在 table

中有以下数据
Period      Total_amount    R_total
01/01/20    2               2
01/02/20    5               null
01/03/20    3               null
01/04/20    8               null
01/05/20    31              null

根据以上数据,我想有以下情况。

Period      Total_amount    R_total
01/01/20    2               2
01/02/20    5               3
01/03/20    3               0
01/04/20    8               8
01/05/20    31              23

附加数据

01/06/20    21              0 (previously it would be -2)
01/07/20    25              25
01/08/20    29              4

附加数据的模式是: 如果 total_amount < previous(r_total) 那么 0

根据填充的数据,我们可以发现模式是: R_total = total_amount - 上一个(R_total)

你能帮我解决这个问题吗?

使用 window 函数可能可行,但最简单的方法可能是递归 CTE:

with t as (
      select t.*, row_number() over (order by period) as seqnum
      from yourtable t
     ),
     cte(period, total_amount, r_amount, seqnum) as (
      select period, total_amount, r_amount, seqnum
      from t
      where seqnum = 1
      union all
      select t.period, t.total_amount, t.total_amount - cte.r_amount, t.seqnum
      from cte join
           t
           on t.seqnum = cte.seqnum + 1
     )
select *
from cte;

这个问题明确地讨论了“递归地”添加值。如果你想用其他机制解决这个问题,你可能会详细解释逻辑并询问是否有非递归CTE解决方案。

正如 Gordon Linoff 所怀疑的那样,用解析函数可以解决这个问题。好处是查询可能会快得多。为这种好处付出的代价是你需要事先做一些数学运算(在考虑“编程”和“计算机”之前)。

一些初等算术表明 R_TOTAL 是 TOTAL_AMOUNT 的交替和。这可以通过使用 ROW_NUMBER()(获取符号)然后使用解析 SUM() 轻松安排,如下所示。

Table 设置:

create table sample_data (period, total_amount) as
  select to_date('01/01/20', 'mm/dd/rr'),  2 from dual union all
  select to_date('01/02/20', 'mm/dd/rr'),  5 from dual union all
  select to_date('01/03/20', 'mm/dd/rr'),  3 from dual union all
  select to_date('01/04/20', 'mm/dd/rr'),  8 from dual union all
  select to_date('01/05/20', 'mm/dd/rr'), 31 from dual
;

查询和结果:

with
  prep (period, total_amount, sgn) as ( 
    select period, total_amount, 
           case mod(row_number() over (order by period), 2) when 0 then 1 else -1 end
    from   sample_data
  )
select period, total_amount,
       sgn * sum(sgn * total_amount) over (order by period) as r_total
from   prep
;

PERIOD   TOTAL_AMOUNT    R_TOTAL
-------- ------------ ----------
01/01/20            2          2
01/02/20            5          3
01/03/20            3          0
01/04/20            8          8
01/05/20           31         23