Oracle SQL - 用于计算贷款未来余额的模型条款
Oracle SQL - Model Clause for Calculating Loan Future Balance
我正在尝试计算一笔贷款的未来余额。我对我正在使用的 Oracle 数据库没有写权限,所以我不能使用 create table。我将有权访问包括账户 ID、当前余额、利率、支付金额、支付频率和剩余摊销在内的账户信息。根据这些信息,我想计算一笔特定付款的贷款余额。以下是我现在正在做的工作:
WITH mortgage_details (account_id, start_date, balance, annual_interest, pmt, pmt_freq, remain_amort) AS (
SELECT 1, TRUNC(SYSDATE, 'MM'), 34798, 2, 670, 'BW', 5 FROM DUAL UNION ALL
SELECT 2, TRUNC(SYSDATE, 'MM'), 50000, 10, 660, 'M', 6 FROM DUAL
),
mtg_final as
(
select mtg.*,
case when pmt_freq = 'BW' then 26
when pmt_freq = 'M' then 12
end as pmt_freq_updated
from mortgage_details mtg
)
select * from mtg_final
where account_id = 1
MODEL
DIMENSION BY (rownum rownumber)
-- Change Amount, Payment and Interest Rate here!
MEASURES (balance, 0 b, 0 c, pmt d, annual_interest e)
RULES SEQUENTIAL ORDER ITERATE (500) UNTIL (balance[ITERATION_NUMBER + 1] <= 0) (
b[ITERATION_NUMBER + 1] = balance[cv(rownumber)] * e[cv(rownumber)] / 1200,
d[ITERATION_NUMBER + 1] = least(d[cv(rownumber)], balance[cv(rownumber)] + b[cv(rownumber)]),
c[ITERATION_NUMBER + 1] = d[cv(rownumber)] - b[cv(rownumber)],
balance[ITERATION_NUMBER + 2] = balance[cv(rownumber)-1] - c[cv(rownumber) - 1],
e[ITERATION_NUMBER + 2] = e[cv(rownumber)-1],
d[ITERATION_NUMBER + 2] = d[cv(rownumber)-1]
)
order by rownumber
但是,问题是我没有使用 table 中的 pmt_freq 和 remain_amort 字段,而是硬编码了 1200。我想将此 1200 替换为类似于 pmt_freq* remain_amort 但是当我尝试这个时我得到了一个错误“这里不允许列”。这是我遇到该错误时尝试的方法:
WITH mortgage_details (account_id, start_date, balance, annual_interest, pmt, pmt_freq, remain_amort) AS (
SELECT 1, TRUNC(SYSDATE, 'MM'), 34798, 2, 670, 'BW', 5 FROM DUAL UNION ALL
SELECT 2, TRUNC(SYSDATE, 'MM'), 50000, 10, 660, 'M', 6 FROM DUAL
),
mtg_final as
(
select mtg.*,
case when pmt_freq = 'BW' then 26
when pmt_freq = 'M' then 12
end as pmt_freq_updated
from mortgage_details mtg
)
select * from mtg_final
where account_id = 1
MODEL
DIMENSION BY (rownum rownumber)
-- Change Amount, Payment and Interest Rate here!
MEASURES (balance, 0 b, 0 c, pmt d, annual_interest e)
RULES SEQUENTIAL ORDER ITERATE (500) UNTIL (balance[ITERATION_NUMBER + 1] <= 0) (
b[ITERATION_NUMBER + 1] = balance[cv(rownumber)] * e[cv(rownumber)] / (100*remain_amort*pmt_freq_updated),
d[ITERATION_NUMBER + 1] = least(d[cv(rownumber)], balance[cv(rownumber)] + b[cv(rownumber)]),
c[ITERATION_NUMBER + 1] = d[cv(rownumber)] - b[cv(rownumber)],
balance[ITERATION_NUMBER + 2] = balance[cv(rownumber)-1] - c[cv(rownumber) - 1],
e[ITERATION_NUMBER + 2] = e[cv(rownumber)-1],
d[ITERATION_NUMBER + 2] = d[cv(rownumber)-1]
)
order by rownumber
在不回顾您的整个方法的情况下,一种非常接近您已有方法的方法是将 pmt_freq
和 remain_amort
添加到 PARTITION
子句中 MODEL
.
(如果您曾经 运行 一次查询多个帐户,您可能想要 PARTITION BY account_id
。
这是它的样子:
WITH mortgage_details (account_id, start_date, balance, annual_interest, pmt, pmt_freq, remain_amort) AS (
SELECT 1, TRUNC(SYSDATE, 'MM'), 34798, 2, 670, 'BW', 5 FROM DUAL UNION ALL
SELECT 2, TRUNC(SYSDATE, 'MM'), 50000, 10, 660, 'M', 6 FROM DUAL
)
select *
from mortgage_details
where account_id = 1
MODEL
-- Add the partition to keep results in your model separated by account_id
-- Since account_id uniquely identifies pmt_freq and remain_amort, we can
-- safely put them here too
PARTITION BY (account_id, pmt_freq, remain_amort)
DIMENSION BY (rownum rownumber)
-- Change Amount, Payment and Interest Rate here!
MEASURES (balance, 0 b, 0 c, pmt d, annual_interest e)
RULES SEQUENTIAL ORDER ITERATE (500) UNTIL (balance[ITERATION_NUMBER + 1] <= 0) (
b[ITERATION_NUMBER + 1] = balance[cv(rownumber)] * e[cv(rownumber)] / (100 * DECODE(cv(pmt_freq),'BW',26,'M',12,0)*cv(remain_amort)),
d[ITERATION_NUMBER + 1] = least(d[cv(rownumber)], balance[cv(rownumber)] + b[cv(rownumber)]),
c[ITERATION_NUMBER + 1] = d[cv(rownumber)] - b[cv(rownumber)],
balance[ITERATION_NUMBER + 2] = balance[cv(rownumber)-1] - c[cv(rownumber) - 1],
e[ITERATION_NUMBER + 2] = e[cv(rownumber)-1],
d[ITERATION_NUMBER + 2] = d[cv(rownumber)-1]
)
order by rownumber
您可以将 pmt_freq_updated
和 remain_amort
添加到 MEASURES
子句,然后,如果需要,使用 pmt_freq_updated[1]
和 remain_amort[1]
引用它们在规则中(因为当您可以在第一行中引用它们时,不需要通过模型的所有迭代传播静态值)。
如果您为列使用有意义的名称,也会有所帮助。
WITH mortgage_details (
account_id,
start_date,
balance,
annual_interest,
pmt,
pmt_freq,
remain_amort
) AS (
SELECT 1, TRUNC(SYSDATE, 'MM'), 34798, 2, 670, 'BW', 5 FROM DUAL UNION ALL
SELECT 2, TRUNC(SYSDATE, 'MM'), 50000, 10, 660, 'M', 6 FROM DUAL
),
mtg_final as
(
select mtg.*,
case
when pmt_freq = 'BW' then 26
when pmt_freq = 'M' then 12
end as pmt_freq_updated
from mortgage_details mtg
)
SELECT account_id,
key,
ROUND(balance, 2) AS balance,
dt,
ROUND(interest_amt, 2) AS interest_amt,
payment,
pmt_freq_updated,
remain_amort
FROM mtg_final
--where account_id = 1
MODEL
PARTITION BY (account_id)
DIMENSION BY (1 AS key)
MEASURES (
balance,
start_date dt,
pmt_freq,
pmt,
1 + annual_interest/100 AS annual_interest,
0 AS interest_amt,
0 AS payment,
pmt_freq_updated,
remain_amort
)
RULES SEQUENTIAL ORDER
ITERATE (100) UNTIL (balance[ITERATION_NUMBER] <= 0)
(
dt[ITERATION_NUMBER+2] = dt[ITERATION_NUMBER+1] + INTERVAL '1' DAY,
annual_interest[key>1] = annual_interest[1],
interest_amt[key] = balance[cv(key)]
* (
POWER(
annual_interest[1],
1/(ADD_MONTHS(TRUNC(dt[cv(key)], 'YYYY'), 12)
- TRUNC(dt[cv(key)], 'YYYY'))
) - 1
),
payment[key]
= CASE
WHEN (pmt_freq[1] = 'M' AND dt[cv(key)] = LAST_DAY(dt[cv(key)]))
OR (pmt_freq[1] = 'BW' AND MOD(cv(key), 14) = 0)
OR (pmt_freq[1] = 'W' AND MOD(cv(key), 7) = 0)
THEN pmt[1]
END,
balance[key>1] = balance[cv(key)-1]
+ interest_amt[cv(key) - 1]
- COALESCE(payment[cv(key)], 0)
)
order by account_id, dt
db<>fiddle here
我正在尝试计算一笔贷款的未来余额。我对我正在使用的 Oracle 数据库没有写权限,所以我不能使用 create table。我将有权访问包括账户 ID、当前余额、利率、支付金额、支付频率和剩余摊销在内的账户信息。根据这些信息,我想计算一笔特定付款的贷款余额。以下是我现在正在做的工作:
WITH mortgage_details (account_id, start_date, balance, annual_interest, pmt, pmt_freq, remain_amort) AS (
SELECT 1, TRUNC(SYSDATE, 'MM'), 34798, 2, 670, 'BW', 5 FROM DUAL UNION ALL
SELECT 2, TRUNC(SYSDATE, 'MM'), 50000, 10, 660, 'M', 6 FROM DUAL
),
mtg_final as
(
select mtg.*,
case when pmt_freq = 'BW' then 26
when pmt_freq = 'M' then 12
end as pmt_freq_updated
from mortgage_details mtg
)
select * from mtg_final
where account_id = 1
MODEL
DIMENSION BY (rownum rownumber)
-- Change Amount, Payment and Interest Rate here!
MEASURES (balance, 0 b, 0 c, pmt d, annual_interest e)
RULES SEQUENTIAL ORDER ITERATE (500) UNTIL (balance[ITERATION_NUMBER + 1] <= 0) (
b[ITERATION_NUMBER + 1] = balance[cv(rownumber)] * e[cv(rownumber)] / 1200,
d[ITERATION_NUMBER + 1] = least(d[cv(rownumber)], balance[cv(rownumber)] + b[cv(rownumber)]),
c[ITERATION_NUMBER + 1] = d[cv(rownumber)] - b[cv(rownumber)],
balance[ITERATION_NUMBER + 2] = balance[cv(rownumber)-1] - c[cv(rownumber) - 1],
e[ITERATION_NUMBER + 2] = e[cv(rownumber)-1],
d[ITERATION_NUMBER + 2] = d[cv(rownumber)-1]
)
order by rownumber
但是,问题是我没有使用 table 中的 pmt_freq 和 remain_amort 字段,而是硬编码了 1200。我想将此 1200 替换为类似于 pmt_freq* remain_amort 但是当我尝试这个时我得到了一个错误“这里不允许列”。这是我遇到该错误时尝试的方法:
WITH mortgage_details (account_id, start_date, balance, annual_interest, pmt, pmt_freq, remain_amort) AS (
SELECT 1, TRUNC(SYSDATE, 'MM'), 34798, 2, 670, 'BW', 5 FROM DUAL UNION ALL
SELECT 2, TRUNC(SYSDATE, 'MM'), 50000, 10, 660, 'M', 6 FROM DUAL
),
mtg_final as
(
select mtg.*,
case when pmt_freq = 'BW' then 26
when pmt_freq = 'M' then 12
end as pmt_freq_updated
from mortgage_details mtg
)
select * from mtg_final
where account_id = 1
MODEL
DIMENSION BY (rownum rownumber)
-- Change Amount, Payment and Interest Rate here!
MEASURES (balance, 0 b, 0 c, pmt d, annual_interest e)
RULES SEQUENTIAL ORDER ITERATE (500) UNTIL (balance[ITERATION_NUMBER + 1] <= 0) (
b[ITERATION_NUMBER + 1] = balance[cv(rownumber)] * e[cv(rownumber)] / (100*remain_amort*pmt_freq_updated),
d[ITERATION_NUMBER + 1] = least(d[cv(rownumber)], balance[cv(rownumber)] + b[cv(rownumber)]),
c[ITERATION_NUMBER + 1] = d[cv(rownumber)] - b[cv(rownumber)],
balance[ITERATION_NUMBER + 2] = balance[cv(rownumber)-1] - c[cv(rownumber) - 1],
e[ITERATION_NUMBER + 2] = e[cv(rownumber)-1],
d[ITERATION_NUMBER + 2] = d[cv(rownumber)-1]
)
order by rownumber
在不回顾您的整个方法的情况下,一种非常接近您已有方法的方法是将 pmt_freq
和 remain_amort
添加到 PARTITION
子句中 MODEL
.
(如果您曾经 运行 一次查询多个帐户,您可能想要 PARTITION BY account_id
。
这是它的样子:
WITH mortgage_details (account_id, start_date, balance, annual_interest, pmt, pmt_freq, remain_amort) AS (
SELECT 1, TRUNC(SYSDATE, 'MM'), 34798, 2, 670, 'BW', 5 FROM DUAL UNION ALL
SELECT 2, TRUNC(SYSDATE, 'MM'), 50000, 10, 660, 'M', 6 FROM DUAL
)
select *
from mortgage_details
where account_id = 1
MODEL
-- Add the partition to keep results in your model separated by account_id
-- Since account_id uniquely identifies pmt_freq and remain_amort, we can
-- safely put them here too
PARTITION BY (account_id, pmt_freq, remain_amort)
DIMENSION BY (rownum rownumber)
-- Change Amount, Payment and Interest Rate here!
MEASURES (balance, 0 b, 0 c, pmt d, annual_interest e)
RULES SEQUENTIAL ORDER ITERATE (500) UNTIL (balance[ITERATION_NUMBER + 1] <= 0) (
b[ITERATION_NUMBER + 1] = balance[cv(rownumber)] * e[cv(rownumber)] / (100 * DECODE(cv(pmt_freq),'BW',26,'M',12,0)*cv(remain_amort)),
d[ITERATION_NUMBER + 1] = least(d[cv(rownumber)], balance[cv(rownumber)] + b[cv(rownumber)]),
c[ITERATION_NUMBER + 1] = d[cv(rownumber)] - b[cv(rownumber)],
balance[ITERATION_NUMBER + 2] = balance[cv(rownumber)-1] - c[cv(rownumber) - 1],
e[ITERATION_NUMBER + 2] = e[cv(rownumber)-1],
d[ITERATION_NUMBER + 2] = d[cv(rownumber)-1]
)
order by rownumber
您可以将 pmt_freq_updated
和 remain_amort
添加到 MEASURES
子句,然后,如果需要,使用 pmt_freq_updated[1]
和 remain_amort[1]
引用它们在规则中(因为当您可以在第一行中引用它们时,不需要通过模型的所有迭代传播静态值)。
如果您为列使用有意义的名称,也会有所帮助。
WITH mortgage_details (
account_id,
start_date,
balance,
annual_interest,
pmt,
pmt_freq,
remain_amort
) AS (
SELECT 1, TRUNC(SYSDATE, 'MM'), 34798, 2, 670, 'BW', 5 FROM DUAL UNION ALL
SELECT 2, TRUNC(SYSDATE, 'MM'), 50000, 10, 660, 'M', 6 FROM DUAL
),
mtg_final as
(
select mtg.*,
case
when pmt_freq = 'BW' then 26
when pmt_freq = 'M' then 12
end as pmt_freq_updated
from mortgage_details mtg
)
SELECT account_id,
key,
ROUND(balance, 2) AS balance,
dt,
ROUND(interest_amt, 2) AS interest_amt,
payment,
pmt_freq_updated,
remain_amort
FROM mtg_final
--where account_id = 1
MODEL
PARTITION BY (account_id)
DIMENSION BY (1 AS key)
MEASURES (
balance,
start_date dt,
pmt_freq,
pmt,
1 + annual_interest/100 AS annual_interest,
0 AS interest_amt,
0 AS payment,
pmt_freq_updated,
remain_amort
)
RULES SEQUENTIAL ORDER
ITERATE (100) UNTIL (balance[ITERATION_NUMBER] <= 0)
(
dt[ITERATION_NUMBER+2] = dt[ITERATION_NUMBER+1] + INTERVAL '1' DAY,
annual_interest[key>1] = annual_interest[1],
interest_amt[key] = balance[cv(key)]
* (
POWER(
annual_interest[1],
1/(ADD_MONTHS(TRUNC(dt[cv(key)], 'YYYY'), 12)
- TRUNC(dt[cv(key)], 'YYYY'))
) - 1
),
payment[key]
= CASE
WHEN (pmt_freq[1] = 'M' AND dt[cv(key)] = LAST_DAY(dt[cv(key)]))
OR (pmt_freq[1] = 'BW' AND MOD(cv(key), 14) = 0)
OR (pmt_freq[1] = 'W' AND MOD(cv(key), 7) = 0)
THEN pmt[1]
END,
balance[key>1] = balance[cv(key)-1]
+ interest_amt[cv(key) - 1]
- COALESCE(payment[cv(key)], 0)
)
order by account_id, dt
db<>fiddle here