SQL 查询可以计算包含利息和本金余额的贷款账户报表吗?

Can a SQL query compute a loan account statement with interest and principal balances?

我有一个 table 与借款人的贷款支付。 table 包含贷款发放日期、发放金额、贷款利率(每月)以及每期付款的日期和金额。

借款人并不总是支付相同的金额,也不会在每月的同一天支付。因此,我公司管理到期利息的方式是计算付款日期之间的天数差异(不包括第一期付款,天数差异来自授予日期和付款日期)。以下是虚构借贷者的摘录:

Payment_Number Date_granted Amount_Granted Interest_Rate Payment_Date Payment_Amount
1 2021-01-01 100000 0.03 2021-01-11 1,100.00
2 2021-01-01 100000 0.03 2021-01-31 2,000.00
3 2021-01-01 100000 0.03 2021-03-02 800.00

我的问题是在计算贷款账户报表时。对于第一行,我知道应计利息是:

Amount_Granted * (Interest Rate / 30) * (Payment_Date - Date_Granted)

但是,我不能对所有其他分期付款都这样做,因为如果第一期的一部分转到本金,则必须使用这个新的本金而不是 Amount_Granted 来计算新的应计利息这个:

Payment_Number Date_granted Amount_Granted Interest_Rate Payment_Date Payment_Amount Accrued_interests Interests_Paid Principal_Paid New_Principal
1 2021-01-01 100000 0.03 2021-01-11 1,100.00 1,000.00 1,000.00 100.00 99,900.00
2 2021-01-01 100000 0.03 2021-01-31 2,000.00 1,998.00 1,998.00 2.00 99,898.00
3 2021-01-01 100000 0.03 2021-03-02 800.00 2,996.94 800.00 0.00 99,898.00

在Excel中,这很容易。但我在 SQL 中努力做到这一点。到目前为止,我已经尝试做一个 CASE WHEN,例如:

SELECT 
    *,
    CASE 
       WHEN Payment_Number = 1 
           THEN (Amount_Granted * (Interest_Rate / 30) * (Payment_Date - Date_Granted)) 
           ELSE (New_Principal * (Interest_Rate / 30) * (LAG(Payment_Date,1) - Payment_Date)) 
    END AS Accrued_Interests,
    CASE 
        WHEN Accrued_Interests < Payment_Amount 
            THEN Accrued_Interests 
            ELSE Payment_Amount 
    END AS Interests_Paid,
    CASE 
        WHEN Accrued_Interests < Payment_Amount 
            THEN 0 
            ELSE Payment_Amount - Interests_Paid 
    END AS Principal_Paid,
    CASE 
        WHEN Payment_Number = 1 
            THEN Amount_Granted - Principal_Paid 
            ELSE New_Principal - LAG(New_Principal,1) - Principal_Paid 
    END AS New_Principal
FROM 
    MY_TABLE

但这会产生错误,因为当我尝试在 Accrued Interests 中使用它时 New_Principal 尚未计算出来。在 SQLFiddle 中,我得到“Unknown column 'New_Principal' in 'field list'”。我也尝试过 JOINS 但没有成功,主要是因为我对 SQL 的经验很少,不知道如何做到这一点。

拜托,我想问问你的方向。下面,SQLFiddle Link 的可重现示例:

SQLFiddle table

而 SQLFiddle Link 错误:

SQLFiddle error

PS:我知道LAG功能不完善,我只是想简化一下。另外:未完全支付的应计利息进入利息余额,但为了简化起见,我忽略了它们。

您示例中的数字有点偏差,并没有真正遵循您指定的公式。我调整了数字以演示 SQL 服务器如何根据您的算法执行计算。

您可以使用递归 CTE 根据日期和上一次付款的更新本金计算每笔付款,如:

with
p as (
  select *,
    payment_amount - interest_paid as principal_paid,
    amount_granted - (payment_amount - interest_paid) as new_principal, cast(null as float) as prev_principal
  from (
    select *,
      case when payment_amount < accrued_interest then payment_amount else accrued_interest end as interest_paid
    from (
      select
        Payment_Number, Date_granted, Amount_Granted,
        Interest_Rate, Payment_Date, Payment_Amount,
        Amount_Granted * (Interest_Rate / 30) * datediff(day, date_granted, payment_date) as accrued_interest
      from account where payment_number = 1
    ) x
  ) y
 union all
  select
    Payment_Number, Date_granted, Amount_Granted,
    Interest_Rate, Payment_Date, Payment_Amount,
    accrued_interest, interest_paid,
    payment_amount - interest_paid,
    prev_principal - (payment_amount - interest_paid),
    prev_principal
  from (
    select *,
      case when payment_amount < accrued_interest then payment_amount else accrued_interest end as interest_paid
    from (
      select a.Payment_Number, a.Date_granted, a.Amount_Granted,
        a.Interest_Rate, a.Payment_Date, a.Payment_Amount,
        p.new_principal * (a.Interest_Rate / 30) * datediff(day, a.date_granted, a.payment_date) as accrued_interest,
        p.new_principal as prev_principal
      from p
      join account a on a.payment_number = p.payment_number + 1
    ) z
  ) w
)
select * from p;

结果:

Payment_Number  Date_granted  Amount_Granted  Interest_Rate  Payment_Date  Payment_Amount  accrued_interest  interest_paid  principal_paid  new_principal  prev_principal
--------------  ------------  --------------  -------------  ------------  --------------  ----------------  -------------  --------------  -------------  --------------
             1   2021-01-01          100,000           0.03   2021-01-11            1,100             1,000          1,000             100         99,900          <null>
             2   2021-01-01          100,000           0.03   2021-01-31            3,000             2,997          2,997               3         99,897          99,900
             3   2021-01-01          100,000           0.03   2021-03-02            2,800          5,993.82          2,800               0         99,897          99,897

请参阅 db<>fiddle 中的 运行 示例。