通过重新分配已支付金额重新生成贷款支付 table

Regenerate the loan payment table by redistribution of paid amounts

我的table个人有如下的分期记录。最初,本金金额指定为 2500,但在一段时间后金额增加 1000 并达到 3500。现在应重新分配付款 table。最初每期支付 2500,我需要分配这笔金额,以便新的分期金额总和为 3500。

--Initial Records
CREATE TABLE #LOAN_REPAYMENT
( 
TRAN_DATE DATETIME,
INSTALLMENT_NO INT,
PAID_AMOUNT DECIMAL(18,2)
)

INSERT INTO #LOAN_REPAYMENT VALUES ('1/15/2016', 11, 2000)
GO
INSERT INTO #LOAN_REPAYMENT VALUES ('2/15/2016', 11, 500)
GO  
INSERT INTO #LOAN_REPAYMENT VALUES ('3/15/2016', 12, 700)
GO
INSERT INTO #LOAN_REPAYMENT VALUES ('3/28/2016', 12, 1800)
GO
INSERT INTO #LOAN_REPAYMENT VALUES ('3/28/2016', 13, 2500)
GO
INSERT INTO #LOAN_REPAYMENT VALUES ('4/15/2016', 14, 2500)
GO
INSERT INTO #LOAN_REPAYMENT VALUES ('4/15/2016', 15, 1500)
GO

结果应该是这样的

CREATE TABLE #LOAN_REPAYMENT_NEW
( 
TRAN_DATE DATETIME,
INSTALLMENT_NO INT,
PAID_AMOUNT DECIMAL(18,2)
)

INSERT INTO #LOAN_REPAYMENT_NEW VALUES ('1/15/2016', 11, 2000)
GO
INSERT INTO #LOAN_REPAYMENT_NEW VALUES ('2/15/2016', 11, 500)
GO  
INSERT INTO #LOAN_REPAYMENT_NEW VALUES ('3/15/2016', 11, 700)
GO
INSERT INTO #LOAN_REPAYMENT_NEW VALUES ('3/28/2016', 11, 300)
GO
INSERT INTO #LOAN_REPAYMENT_NEW VALUES ('3/28/2016', 12, 1500)
GO
INSERT INTO #LOAN_REPAYMENT_NEW VALUES ('3/28/2016', 12, 2000)
GO
INSERT INTO #LOAN_REPAYMENT_NEW VALUES ('3/28/2016', 13, 500)
GO
INSERT INTO #LOAN_REPAYMENT_NEW VALUES ('4/15/2016', 13, 2500)
GO
INSERT INTO #LOAN_REPAYMENT_NEW VALUES ('4/15/2016', 13, 500)
GO
INSERT INTO #LOAN_REPAYMENT_NEW VALUES ('4/15/2016', 14, 1000)
GO

我假设最后一条记录的 installment_no 值为 14(结果截图显示 13,结果代码显示 14)。我在代码注释中包含了我对答案的解释。

if object_id('tempdb..#loan_repayment') is not null
    drop table #loan_repayment

if object_id('tempdb..#loan_repayment_final') is not null
    drop table #loan_repayment_final

CREATE TABLE #LOAN_REPAYMENT
( 
TRAN_DATE DATETIME,
INSTALLMENT_NO INT,
PAID_AMOUNT DECIMAL(18,2)
)

--creating the temp table for final results
CREATE TABLE #LOAN_REPAYMENT_FINAL
( 
TRAN_DATE DATETIME,
INSTALLMENT_NO INT,
PAID_AMOUNT DECIMAL(18,2),
TRAN_RANK INT,
ITER_VAL INT
)

--inserting inital sample data
INSERT INTO #LOAN_REPAYMENT VALUES ('1/15/2016', 11, 2000)
GO
INSERT INTO #LOAN_REPAYMENT VALUES ('2/15/2016', 11, 500)
GO  
INSERT INTO #LOAN_REPAYMENT VALUES ('3/15/2016', 12, 700)
GO
INSERT INTO #LOAN_REPAYMENT VALUES ('3/28/2016', 12, 1800)
GO
INSERT INTO #LOAN_REPAYMENT VALUES ('3/28/2016', 13, 2500)
GO
INSERT INTO #LOAN_REPAYMENT VALUES ('4/15/2016', 14, 2500)
GO
INSERT INTO #LOAN_REPAYMENT VALUES ('4/15/2016', 15, 1500)
GO

declare @install_no int = (select min(installment_no) from #loan_repayment) --starting installment_no
declare @install_amt decimal(18,2) = 3500.00 --new installment amount
declare @rec_rank int --used in while loop
declare @i int = 0 --used to track iteration of while loop

if object_id('tempdb..#loan_repayment_rank') is not null
    drop table #loan_repayment_rank

--adding additional derived columns to the initial sample data
select lr.tran_date
, lr.installment_no
, lr.paid_amount
, sum(lr.paid_amount) over (order by lr.tran_date asc, lr.installment_no asc) as unassigned_amt
, row_number() over (order by lr.tran_date asc, lr.installment_no asc) as tran_rank
into #loan_repayment_rank
from #loan_repayment as lr

while exists (select *
              from #loan_repayment_rank as lr
              where 1=1
              and lr.unassigned_amt > 0)
begin

    --finding the highest record rank that should be included in
    --the current installment calculation
    set @rec_rank = coalesce((select min(lrr.tran_rank)
                                from #loan_repayment_rank as lrr
                                where 1=1
                                and unassigned_amt >= @install_amt)
                            , (select min(lrr.tran_rank)
                                from #loan_repayment_rank as lrr
                                where 1=1
                                and unassigned_amt > 0))

    --adding the necessary records to the fianl output
    insert into #loan_repayment_final
    select lrr.tran_date
    , @install_no + @i as installment_no
    , case when lrr.tran_rank < @rec_rank then lrr.paid_amount
           when lrr.tran_rank = @rec_rank and lrr.paid_amount = lrr.unassigned_amt then lrr.paid_amount
           else @install_amt - (select a.unassigned_amt from #loan_repayment_rank as a where tran_rank = @rec_rank - 1)
      end as paid_amount
    , lrr.tran_rank
    , @i as iter_val
    from #loan_repayment_rank as lrr
    where 1=1
    and lrr.tran_rank <= @rec_rank
    and lrr.unassigned_amt > 0

    --decrementing the paid amounts on the source temp table
    --on the records effected by the current installment calculation
    update lr
    set lr.paid_amount = lr.paid_amount - lf.paid_amount
    from #loan_repayment_final as lf
    right join #loan_repayment_rank as lr on lf.tran_rank = lr.tran_rank
    where 1=1
    and lf.iter_val = @i

    --updating the unassigned amount on all records
    update lr
    set lr.unassigned_amt = case when lr.unassigned_amt <= @install_amt then 0.00
                                 else lr.unassigned_amt - @install_amt
                            end
    from #loan_repayment_rank as lr

    --incrementing the iteration
    set @i += 1

end

--final output
select lf.tran_date
, lf.installment_no
, lf.paid_amount
from #loan_repayment_final as lf