Sql 如何从日期计算费率
我正在创建一个函数,它将根据费率和日期之间的天数计算金额 通过循环,我需要检查哪个速率适合v_日期Sql 如何从日期计算费率,sql,oracle,plsql,Sql,Oracle,Plsql,我正在创建一个函数,它将根据费率和日期之间的天数计算金额 通过循环,我需要检查哪个速率适合v_日期 CREATE TABLE INTEREST_RATE_TAB ( ID VARCHAR2(4 BYTE) NOT NULL, NAME VARCHAR2(100 BYTE) NOT NULL, RATE NUMBER NOT NUL
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_日期为2000年1月1日,并且
付款日期为2000年1月3日。然后循环中的v_数量将为1.2*
60 03.01.2000-01.01.2000
但如果它包含两个或多个时段,例如v_date为
2016年1月12日,v_日期付款为2018年1月1日,则循环的v_金额为1,2*23 2016年12月23日-2016年1月12日,然后v_金额为0.2*
332 2017年11月21日-2016年12月24日,然后v_金额=1,2*39 2018年1月1日
-2017年11月22日
我想我知道你需要什么。不需要循环,因为一个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;
欢迎来到SO。你需要把你尝试过的东西和你被困的地方贴出来。社区专家将帮助解决这个问题。