SQL Server Apply cumulative discount to a table

t5fffqht  于 2023-05-21  发布在  其他
关注(0)|答案(2)|浏览(136)

To the table I need to calculate DISCOUNT_AMOUNT and AMOUNT_REST as follows in the formula view. For every NAME there is a unique amount repeated in every row, but the discount isnt AMOUNT*DISCOUNT% in all rows, only the first discount order by ascending DATE . The next discounts for the same NAME are applied to the AMOUNT_REST .

A way to solve this in SQL Server language?

select [NAME], [AMOUNT], [DISCOUNT%], [DATE]--, DISCOUNT_AMOUNT, AMOUNT_REST
FROM [Table]
order by [NAME], [DATE] asc

This is what a I want:

[NAME][AMOUNT][DATE] (dd-MM-yyyy)[DISCOUNT%][DISCOUNT_AMOUNT][AMOUNT_REST]
Peter$10001-01-20234%$4,0$96,0
Peter$10002-01-202320%$19,2$76,8
Peter$10003-01-20235%$3,8$73,0
John$50001-01-202340%$200,0$300,0
John$50002-01-20233%$9,0$291,0
Sara$20001-01-20239%$18,0$182,0
Sara$20002-01-202310%$18,2$163,8

This is the Excel-manual way to do it

[NAME][AMOUNT][DATE] (dd-MM-yyyy)[DISCOUNT%][DISCOUNT_AMOUNT][AMOUNT_REST]
Peter100449270,04B2*D2B2*(1-D2)
Peter100449280,2F2*D3F2*(1-D3)
Peter100449290,05F3*D4F3*(1-D4)
John500449270,4B5*D5B5*(1-D5)
John500449280,03F5*D6F5*(1-D6)
Sara200449270,09B7*D7B7*(1-D7)
Sara200449280,1F7*D8F7*(1-D8)
ar7v8xwq

ar7v8xwq1#

Although there might be a mathematical approach to compute the cumulative discount using window functions, this seems more easily approached with a recursive query:

with 
    data as (
        select t.*, row_number() over(partition by name order by date) rn
        from mytable t
    ),
    rcte as (
        select name, amount, date, discount_pct, rn,
            amount * discount_pct       as discount_amount,
            amount * (1 - discount_pct) as rest_amount
        from data
        where rn = 1
        union all
        select r.name, r.amount, d.date, d.discount_pct, d.rn
            r.rest_amount * d.discount_pct,
            r.rest_amount * (1 - d.discount_pct)              
        from rcte r
        inner join data d on d.name = r.name and d.rn = r.rn + 1
    )
select * from rcte

The first CTE, data , enumerates the rows of each name . Then rcte starts from the first row of each partition and iterates, basically applying the same computation logic as the Excel formulas in your question.

hyrbngr7

hyrbngr72#

Math: log (a+b) = log(a) * log(n), so we can calculate exp(sum(log(col))

https://dbfiddle.uk/IZMXyxUq

with data(name, amount, dat, discount) as (
    select 'Peter', 100, convert(DATE, '01-01-2023',105), 4  union all
    select 'Peter', 100, convert(DATE,'02-01-2023',105), 20  union all
    select 'Peter', 100, convert(DATE,'03-01-2023',105),5  union all
    select 'John', 500, convert(DATE,'01-01-2023',105),40  union all
    select 'John', 500, convert(DATE,'02-01-2023',105),3  union all
    select 'Sara', 200, convert(DATE,'01-01-2023',105),9  union all
    select 'Sara', 200, convert(DATE,'02-01-2023',105),10  -- union all
),
rdata as (
    select * from (
        select d.name, d.amount, d.dat, d.discount / 100.0 as discount, 
            log(1.0 - (d.discount / 100.0)) as ln_rest_factor, 
        row_number() over(partition by name order by dat) as rn
        from data d
    ) d
)
select name, amount, discount, coalesce(lag(discount_rest) over(partition by name order by rn),amount) - discount_rest as discount_amount, discount_rest 
from (
    select name, amount, discount, round(amount * exp(sum_factor),2) as discount_rest, rn
    from (
        select name, amount, dat, discount, rn, ln_rest_factor,
            sum(ln_rest_factor) over(partition by name order by rn) as sum_factor
        from rdata
    ) d
) d
order by name, rn
;

相关问题