我创建了一个函数,它将根据日期之间的比率和天数来计算金额。
通过循环,我需要检查哪个速率适合v_date。
CREATE TABLE INTEREST_RATE_TAB
(
ID VARCHAR2(4 BYTE) NOT NULL,
NAME VARCHAR2(100 BYTE) NOT NULL,
RATE NUMBER NOT NULL,
START_DATE DATE NOT NULL,
END_DATE DATE
)
Insert into INTEREST_RATE_TAB
(ID, NAME, RATE, START_DATE, END_DATE)
Values
('1', 'RATE ', 1.2, TO_DATE('01/01/1999 00:00:00', 'MM/DD/YYYY HH24:MI:SS'), TO_DATE('12/23/2016 00:00:00', 'MM/DD/YYYY HH24:MI:SS'));
Insert into INTEREST_RATE_TAB
(ID, NAME, RATE, START_DATE, END_DATE)
Values
('2', 'RATE II', 0.2, TO_DATE('12/24/2016 00:00:00', 'MM/DD/YYYY HH24:MI:SS'), TO_DATE('11/21/2017 00:00:00', 'MM/DD/YYYY HH24:MI:SS');
Insert into INTEREST_RATE_TAB
(ID, NAME, RATE, START_DATE, END_DATE )
Values
('3', 'RATE III', 1.2, TO_DATE('11/22/2017 00:00:00', 'MM/DD/YYYY HH24:MI:SS'));
例如:
如果它仅覆盖一个期间,例如v_date为01.01.2000且 v_date_payout是03.01.2000。那么循环中将有v_amount = 1.2 * 60(03.01.2000-01.01.2000)。
,但如果涵盖两个或多个期间,例如v_date为 2016年1月12日且v_date_payout为01.01.2018,则循环将具有v_amount = 1,2 * 23(12.23.2016-12.01.2016),然后v_amount = 0.2 * 332(11/21 / 2017-12 / 24/2016),然后v_amount = 1,2 * 39(01.01.2018 -2017年11月22日)
答案 0 :(得分:0)
您确实确实需要查看帮助部分中的How to ask部分。以此作为模板通常会大大增加您获得满意答案的机会。要特别注意发布样本数据和该数据的预期结果(而不是仅仅作为段落),还应确保任何插入支持语句确实有效(三个中的两个无效)。但是我想我知道您的需求。顺便说一句,不需要循环,因为单个SQL将解决此问题。提示停止以过程步骤(即循环)的方式思考,而是以集体处理的数据集的方式思考。 第一件事是要意识到您有4个条件可以生成全部或部分预期结果。如下所示。
LET
Rs ==> rate.start_date
Re ==> rate.end_date
Vd ==> v_date
Vp ==> v_date_payout
Case 1: Vd>=Rs & Vp<=Re Days = Vp-Vd
Rs--------------------------Re
Vd---------------Vp
Case 2: Vd<=Rs & (Vp>=Rs & Vp<=Re) Days = Vp-Rs
Rs--------------------------Re
Vd---------------Vp
Case 3: Vd>=Rs # Vp>=Re Days = Re-Vd
Rs--------------------------Re
Vd---------------Vp
Case 4: Vd<Rs & Vp>=Re Days Re-Rs
Rs--------------------------Re
Vd-------------------------------------Vp
Other Cases: Exhibits 2 cases where Vd---Vp lies totally outside Rs----Re.
These will generate NO amount and so not selected for calculation.
Rs--------------Re
Vd----Vp Vd----Vp
该解决方案涉及两个步骤:首先,确定范围Vd ---- Ve与范围Rs ------ Re之间的重叠。在此步骤中,还提取费率和应用费率的天数。其次,为每个贡献范围计算每个范围的部分贡献(可以从最终丢弃)和范围Vp ---- Ve的总和。
with irt as
-- resolve null in end_date default to sysdate
(select id, name, rate,start_date Rs, nvl(end_date,trunc(sysdate)) Re from interest_rate_tab)
-- generate example v_date, v_date_payout sets
, vdata as
( select 'v1' vid,date '2000-01-01' Vd, date '2000-03-01' Vp from dual union all
select 'v2', date '2016-12-01', date '2018-01-01' Vp from dual
)
select t.*
, days*rate range_amount --contribution of each range
, sum(days*rate) over (partition by vid) total_amount -- for vid total
from ( --Determinr Range, extract rate and calculate days to apply rate
-- Vid, rate, days all that's needed, others included for verification purposes, but can e eliminted from final
select vid,Vd,Vp,Rs,Re,Rate
, case when ( Vd >= Rs and Vp < Re ) then Vp-Vd
when ( Vd<=Rs and (Vp>=Rs and Vp<=Re)) then Vp-Rs
when ( Vd>=Rs And Vp>=Re) then Re-Vd
when ( Vd<Rs and Vp>=Re) then Re-Rs
else 0
end days
-- following case staement for display of how daays were derived, for testing, but can e eliminted from fina
, case when ( Vd >= Rs and Vp < Re )
then '( Vd >= Rs and Vp < Re ) ==>Vp-Vd'
when ( Vd<=Rs and (Vp>=Rs and Vp<=Re))
then '( Vd<=Rs and (Vp>=Rs and Vp<=Re)) ==>Vp-Rs'
when ( Vd>=Rs And Vp>=Re)
then '( Vd>=Rs And Vp>=Re) ==>Re-Vd'
when ( Vd<Rs and Vp>=Re)
then '( Vd<Rs and Vp>=Re) ==>Re-Rs'
else 'Error'
end dd
from irt
join vdata
on ( ( Vd >= Rs and Vp < Re )
or ( Vd<=Rs and (Vp>=Rs and Vp<=Re))
or ( Vd>=Rs And Vp>=Re)
or ( Vd<Rs and Vp>=Re)
)
) t
order by vid;
-- resolve null in end_date default to sysdate
(select id, name, rate,start_date Rs, nvl(end_date,trunc(sysdate)) Re from interest_rate_tab)
-- generate example v_date, v_date_payout sets
, vdata as
( select 'v1' vid,date '2000-01-01' Vd, date '2000-03-01' Vp from dual union all
select 'v2', date '2016-12-01', date '2018-01-01' Vp from dual
)
select t.*
, days*rate amount_part --individual section amount
, sum(days*rate) over (partition by vid) --total amount for each vid
from (
with irt as
-- resolve null in end_date default to sysdate
(select id, name, rate,start_date Rs, nvl(end_date,trunc(sysdate)) Re from interest_rate_tab)
-- generate example v_date, v_date_payout sets
, vdata as
( select 'v1' vid,date '2000-01-01' Vd, date '2000-03-01' Vp from dual union all
select 'v2', date '2016-12-01', date '2018-01-01' Vp from dual
)
select vid,Vd,Vp,Rs,Re,Rate
-- determine range overlap -- calculate days
, case when ( Vd >= Rs and Vp < Re ) then Vp-Vd
when ( Vd<=Rs and (Vp>=Rs and Vp<=Re)) then Vp-Rs
when ( Vd>=Rs and Vp>=Re) then Re-Vd
when ( Vd<Rs and Vp>=Re) then Re-Rs
else 0
end days
-- following case statement for display of how days were derived, for testing, when validated delete
, case when ( Vd >= Rs and Vp < Re )
then '( Vd >= Rs and Vp < Re ) ==>Vp-Vd'
when ( Vd<=Rs and (Vp>=Rs and Vp<=Re))
then '( Vd<=Rs and (Vp>=Rs and Vp<=Re)) ==>Vp-Rs'
when ( Vd>=Rs And Vp>=Re)
then '( Vd>=Rs And Vp>=Re) ==>Re-Vd'
when ( Vd<Rs and Vp>=Re)
then '( Vd<Rs and Vp>=Re) ==>Re-Rs'
else 'Error'
end dd
from irt
join vdata
on ( ( Vd >= Rs and Vp < Re )
or ( Vd<=Rs and (Vp>=Rs and Vp<=Re))
or ( Vd>=Rs And Vp>=Re)
or ( Vd<Rs and Vp>=Re)
)
) t
order by vid;
最后是测试完成后的“清理”版本。
with irt as
-- end_datesolve null in end_date default to sysdate
(select id, name, rate,start_date, nvl(end_date,trunc(sysdate)) end_date from interest_rate_tab)
-- generate example v_date, v_date_payout sets
, v_data as
( select 'v1' vid,date '2000-01-01' v_date, date '2000-03-01' v_date_payment from dual union all
select 'v2', date '2016-12-01', date '2018-01-01' v_date_payment from dual
)
select distinct vid, v_date, v_date_payment
, sum(days*rate) over (partition by vid) total_amount -- for vid total
from ( --Determine Range, extract rate, and calculate days to apply rate
select vid,v_date,v_date_payment,start_date,end_date,Rate
, case when ( v_date >= start_date and v_date_payment < end_date ) then v_date_payment-v_date
when ( v_date<=start_date
and v_date_payment>=start_date
and v_date_payment<=end_date
) then v_date_payment-start_date
when ( v_date>=start_date And v_date_payment>=end_date) then end_date-v_date
when ( v_date<start_date and v_date_payment>=end_date) then end_date-start_date
else 0
end days
from irt
join v_data
on ( ( v_date >= start_date and v_date_payment < end_date )
or ( v_date<=start_date and (v_date_payment>=start_date and v_date_payment<=end_date))
or ( v_date>=start_date And v_date_payment>=end_date)
or ( v_date<start_date and v_date_payment>=end_date)
)
) t
order by vid;