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 Link 错误:
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 中的 运行 示例。
我有一个 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 Link 错误:
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 中的 运行 示例。