Search code examples
sqloraclerecursive-query

Oracle SQL recursive adding values


I have the following data in the 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

Based on the above data I would like to have the following situation.

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

Additional data

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

Pattern to the additional data is: if total_amount < previous(r_total) then 0

Based on the filled data, we can spot the pattern is: R_total = total_amount - previous(R_total)

Could you please help me out with this issue?


Solution

  • As Gordon Linoff suspected, it is possible to solve this problem with analytic functions. The benefit is that the query will likely be much faster. The price to pay for that benefit is that you need to do a bit of math beforehand (before ever thinking about "programming" and "computers").

    A bit of elementary arithmetic shows that R_TOTAL is an alternating sum of TOTAL_AMOUNT. This can be arranged easily by using ROW_NUMBER() (to get the signs) and then an analytic SUM(), as shown below.

    Table setup:

    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
    ;
    

    Query and result:

    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