如何从日期计算费率

时间:2019-09-04 11:54:38

标签: sql oracle plsql

我创建了一个函数,它将根据日期之间的比率和天数来计算金额。

通过循环,我需要检查哪个速率适合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日)

1 个答案:

答案 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;