2017-03-03 170 views
1

我有一个表格,用于存储利率,每个表格都有一个开始日期,以便适用。表中的更新条目取代了之前的条目。我必须用开始日期,结束日期和金额查询此表。从这些值中,我需要最终得出一个整体利息额度,并考虑日期跨度的不同利率。跨多个利率计算利息

CREATE TABLE [dbo].[Interest_Rates](
[Interest_Rate] [float] NULL, 
[Incept_Date] [datetime] NULL 
) ON [PRIMARY] 
GO 

我的利率四个“带”:

INSERT [dbo].[Interest_Rates] ([Interest_Rate], [Incept_Date]) VALUES (10, CAST(N'2001-05-03 11:12:16.000' AS DateTime)) 
GO 
INSERT [dbo].[Interest_Rates] ([Interest_Rate], [Incept_Date]) VALUES (11.5, CAST(N'2014-01-07 10:49:28.433' AS DateTime)) 
GO 
INSERT [dbo].[Interest_Rates] ([Interest_Rate], [Incept_Date]) VALUES (13.5, CAST(N'2016-03-01 00:00:00.000' AS DateTime)) 
GO 
INSERT [dbo].[Interest_Rates] ([Interest_Rate], [Incept_Date]) VALUES (15.5, CAST(N'2016-05-01 00:00:00.000' AS DateTime)) 
GO 

我想知道的是,是否有可能来计算一段时间的利率的时候开始例如,利率为11.5%,并且在一次查询中利率两次上涨至13.5%的时间结束。

好像利息计算为每个“频段”可以使用妙Suprotim瓦尔的实例如下进行:

DECLARE @StartDate DateTime 
DECLARE @EndDate DateTime 
DECLARE @Amount Float 

SET @StartDate = '2014-04-22' 
SET @EndDate = '2016-04-13' 
SET @Amount = 150000.00 

SELECT 
@Amount*(POWER(1.1550, CONVERT(NUMERIC(8,3), 
DATEDIFF(d, @StartDate, @EndDate)/365.25))) - @Amount 
as TotalInterest 

(在上面的例子中,在15.5%的利率)

我遇到困难的地方在于计算如何将计算与利率表相关联,以便加入考虑日期跨度的每个子部分落入哪个“带”。

任何帮助或建议将不胜感激。

+0

创建商店d针对这种情况的程序。 –

回答

2

tl; dr:完成的查询是此长解释结尾的最后一个代码块。

让我们逐步讲解,然后将最终解决方案呈现为一个查询。需要几个步骤来解决这个问题。

1)找出哪个酒店我们想要的日期范围涵盖

2)设计一个聪明的方式来选择那些利率

3)结合这些日期和价格以这样的方式给我们,总利息计息。


一些初步的注意

由于利率的实例计算认为天作为其最好的决定,我只是使用的数据类型日期而不是日期时间。如果您需要更高分辨率,请告诉我,我可以更新。

我用以下声明的变量

declare @EndOfTime date = '2049-12-31' -- This is some arbitrary end of time value that I chose 
declare @StartDate Date = '2012-04-22' -- I made this earlier to cover more rates 
declare @EndDate Date = '2016-04-13' 
declare @Amount Float = 100000.00 -- I changed it to a softer number 



1)日期间隔

现在,你interest_rates表列出了日期是这样的:

+ ------------- + ----------- + 
| interest_rate | incept_date | 
+ ------------- + ----------- + 
| 10   | 2001-05-03 | 
| 11.5   | 2014-01-07 | 
| 13.5   | 2016-03-01 | 
| 15.5   | 2016-05-01 | 
+ ------------- + ----------- + 

但你想要它列表的时间间隔是这样的:

+ ------------- + ------------ + ------------ + 
| interest_rate | inter_begin | inter_end | 
+ ------------- + ------------ + ------------ + 
| 10   | 2001-05-03 | 2014-01-06 | 
| 11.5   | 2014-01-07 | 2016-02-29 | 
| 13.5   | 2016-03-01 | 2016-04-30 | 
| 15.5   | 2016-05-01 | 2049-12-31 | 
+ ------------- + ------------ + ------------ + 

下面的查询可以把你的日期列表为间隔:

select i1.interest_rate 
     , i1.incept_date as inter_begin 
     , isnull(min(i2.incept_date) - 1,@EndOfTime) as inter_end 
    from #interest i1 
    left join #interest i2 on i2.incept_date > i1.incept_date 
    group by i1.interest_rate, i1.incept_date 

注:我打得有点松的日期算术这里不使用DATEADD( )命令。

保持跟踪日期间隔,这样可以更轻松地选择适用的费率。


2)选择在价格

现在,我们可以选择坐我们的期望范围内使用上述查询作为CTE的记录。这个查询有点棘手,所以花点时间去真正理解它。

; with 
    intervals as ( 
     -- The above query/table 
    ) 
select * 
    from intervals 
    where inter_begin >= (
     select inter_begin -- selects the first rate covered by our desired interval 
      from intervals 
      where @StartDate between inter_begin and inter_end 
    ) 
     and inter_end <= (
      select inter_end -- selects the last rate covered by our desired interval 
       from intervals 
       where @EndDate between inter_begin and inter_end 
    ) 

这有效地过滤掉我们不关心任何率,给我们留下了

+ ------------- + ------------ + ------------ + 
| interest_rate | inter_begin | inter_end | 
+ ------------- + ------------ + ------------ + 
| 10   | 2001-05-03 | 2014-01-06 | 
| 11.5   | 2014-01-07 | 2016-02-29 | 
| 13.5   | 2016-03-01 | 2016-04-30 | 
+ ------------- + ------------ + ------------ + 


3)计算利息

现在我们所拥有的一切我们需要,计算利益只是从这张表中选择正确的事情。您为计算所写的大部分内容都保持不变;主要变化在于datediff()命令。使用@StartDate@EndDate不会给我们在每个具体费率上花费的精确天数。我们遇到同样的问题,使用inter_begininter_end。相反,我们必须使用一个case语句,像

datediff(day, 
    case when @StartDate > inter_begin then @StartDate else inter_begin end, 
    case when @EndDate < inter_end then @EndDate else inter_end end 
) 

在上面的查询将这个东西拿到

; with 
    intervals as (...) -- same as above 
select * 
     , DATEDIFF(day, 
       case when @StartDate > inter_begin then @StartDate else inter_begin end, 
       case when @EndDate < inter_end then @EndDate else inter_end end) as days_active 
     , @Amount*(POWER((1+interest_rate/100), 
       convert(float, 
        DATEDIFF(day, 
         case when @StartDate > inter_begin then @StartDate else inter_begin end, 
         case when @EndDate < inter_end then @EndDate else inter_end end 
       ) 
      )/365.25) 
     ) - @Amount as Actual_Interest 
    from ... -- same as above 

这给了我们这个表

+ ------------- + ------------ + ------------ + ----------- + --------------- + 
| interest_rate | inter_begin | inter_end | days_active | Actual_interest | 
+ ------------- + ------------ + ------------ + ----------- + --------------- + 
| 10   | 2001-05-03 | 2014-01-06 | 624   | 17683.63  | 
| 11.5   | 2014-01-07 | 2016-02-29 | 786   | 26283.00  | 
| 13.5   | 2016-03-01 | 2016-04-30 | 43   | 1501.98   | 
+ ------------- + ------------ + ------------ + ----------- + --------------- + 

最后,把这个CTE并取Actual_interest字段的总和:

declare @EndOfTime date = '2049-12-31' -- This is some arbitrary end of time value that I chose 
declare @StartDate Date = '2012-04-22' -- I made this earlier to cover more rates 
declare @EndDate Date = '2016-04-13' 
declare @Amount Float = 100000.00 -- I changed it to a softer number 

; with 
    intervals as (
     select i1.interest_rate 
       , i1.incept_date as inter_begin 
       , isnull(min(i2.incept_date) - 1,@EndOfTime) as inter_end 
      from #interest i1 
      left join #interest i2 on i2.incept_date > i1.incept_date 
      group by i1.interest_rate, i1.incept_date 
    ) 
    , interest as (
     select * 
       , DATEDIFF(day, 
         case when @StartDate > inter_begin then @StartDate else inter_begin end, 
         case when @EndDate < inter_end then @EndDate else inter_end end) as days_active 
       , @Amount*(POWER((1+interest_rate/100), 
         convert(float, 
          DATEDIFF(day, 
           case when @StartDate > inter_begin then @StartDate else inter_begin end, 
           case when @EndDate < inter_end then @EndDate else inter_end end 
         ) 
        )/365.25) 
       ) - @Amount as Actual_Interest 
      from intervals 
      where inter_begin >= (
       select inter_begin -- selects the first rate covered by our desired interval 
        from intervals 
        where @StartDate between inter_begin and inter_end 
      ) 
       and inter_end <= (
        select inter_end -- selects the last rate covered by our desired interval 
         from intervals 
         where @EndDate between inter_begin and inter_end 
      ) 
    ) 
select sum(actual_interest) as total_interest 
    from interest 
+1

这是一个很好的答案,因为你以一种非常有用的方式回答了我的问题,并且你详细解释了每一步的每一步。这是我在这里问一个问题时总希望得到的答案。非常感谢! –

1

也许比您寻找的要多一点,但在本例中,您可以在一个查询中计算所有贷款。

还可能发现最后3列,代表总天数,获得后总利息和总加权平均利率

Declare @Interest_Rate table (interest_rate money,Incept_Date datetime) 
Insert Into @Interest_Rate values 
(10 ,'2001-05-03 11:12:16.000'), 
(11.5,'2014-01-07 10:49:28.433'), 
(13.5,'2016-03-01 00:00:00.000'), 
(15.5,'2016-05-01 00:00:00.000') 

Declare @Loan table (Id int,StartDate date, EndDate date,Amount money) 
Insert Into @Loan values 
(1,'2014-01-01','2015-11-17',150000), 
(1,'2015-11-18','2016-12-31',175000), -- Notice Balance Change 
(2,'2016-01-01','2020-06-15',200000) 


Select A.ID 
     ,A.Amount 
     ,DateR1 = min(D) 
     ,DateR2 = max(D) 
     ,Days = count(*) 
     ,B.Interest_Rate 
     ,Interest_Earned = cast(sum(((A.Amount*B.Interest_Rate)/B.DIY)/100.0) as decimal(18,2)) 
     ,Total_Days  = sum(count(*)) over (Partition By A.ID) 
     ,Total_Int_Earned = sum(cast(sum(((A.Amount*B.Interest_Rate)/B.DIY)/100.0) as decimal(18,2))) over (Partition By A.ID) 
     ,Total_WAIR  = sum(A.Amount * count(*) * B.interest_rate) over (Partition By A.ID)/ sum(A.Amount * count(*)) over (Partition By A.ID) 
From @Loan A 
Join (
     Select D 
       ,D1 
       ,interest_rate 
       ,DIY = 365.0 + IIF(Year(D) % 4 = 0 , 1 , 0) 
     From (Select Top (DateDiff(DD,(Select cast(min(Incept_Date) as date) from @Interest_Rate),cast(GetDate() as date))+1) D=DateAdd(DD,-1+Row_Number() Over (Order By (Select NULL)),(Select cast(min(Incept_Date) as date) from @Interest_Rate)) From master..spt_values N1,master..spt_values N2 ) A 
     Join (
       Select interest_rate 
         ,D1 = cast(Incept_Date as Date) 
         ,D2 = cast(DateAdd(DAY,-1,Lead(Incept_Date,1,GetDate()) over (Order by Incept_Date)) as date) 
       From @Interest_Rate 
      ) B on D between D1 and D2 
    ) B on D Between StartDate and EndDate 
    Group By A.ID,A.Amount,B.D1,B.Interest_Rate 

返回

enter image description here