SQL oracle пересчет сумм в неотрицательные

У меня есть договор и даты с суммами начислений.
Начисления могут быть с плюсом и минусом, например:

contract_id = 1 
01.01.2024 100₽
02.01.2024 500₽
03.01.2024 300₽ 
04.01.2024 -700₽ 
05.01.2024 -50₽ 
06.01.2024 50₽ 
07.01.2024 -30₽ 
08.01.2024 100₽ 

Мне нужно написать sql запрос select, который рассчитает результат таким образом, чтобы вместо отрицательных значений в списке начислений уменьшились ближайшие предыдущие положительные значения, чтобы все значения начислений были неотрицательными, как будто не было отрицательных начислений, а положительные были меньше, то есть:

contract_id = 1
01.01.2024 100₽ 
02.01.2024 50₽
03.01.2024 0₽ 
04.01.2024 0₽ 
05.01.2024 0₽ 
06.01.2024 20₽
07.01.2024 0₽
08.01.2024 100₽

Каким запросом это можно сделать?


Ответы (1 шт):

Автор решения: ESkri

Сначала вычисляете нарастающий итог, потом ищете в нём "будущий минимум":

Подготовка таблицы

create table sums (contract_id number, dt date, amount number);

insert into sums (contract_id, dt, amount)
    select 1 as contract_id, date'2024-01-01' as dt,  100 as amount from dual union all
    select 1 as contract_id, date'2024-01-02' as dt,  500 as amount from dual union all
    select 1 as contract_id, date'2024-01-03' as dt,  300 as amount from dual union all
    select 1 as contract_id, date'2024-01-04' as dt, -700 as amount from dual union all
    select 1 as contract_id, date'2024-01-05' as dt,  -50 as amount from dual union all
    select 1 as contract_id, date'2024-01-06' as dt,   50 as amount from dual union all
    select 1 as contract_id, date'2024-01-07' as dt,  -30 as amount from dual union all
    select 1 as contract_id, date'2024-01-08' as dt,  100 as amount from dual;

Запрос

select
    contract_id,
    dt,
    amount,
    greatest(0, min(acc) over (partition by contract_id order by dt desc) - acc + amount)
        as positive_amount
from
    (
        select
            contract_id,
            dt,
            amount,
            sum(amount) over (partition by contract_id order by dt) as acc
        from
            sums
    )
order by
    contract_id,
    dt
CONTRACT_ID DT AMOUNT POSITIVE_AMOUNT
1 01.01.2024 100 100
1 02.01.2024 500 50
1 03.01.2024 300 0
1 04.01.2024 -700 0
1 05.01.2024 -50 0
1 06.01.2024 50 20
1 07.01.2024 -30 0
1 08.01.2024 100 100
→ Ссылка