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_freqremain_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_updatedremain_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