计算多个利率的利息

时间:2017-03-03 10:41:30

标签: sql-server tsql calculation

我有一张表存储利率,每个利率都有一个适用的开始日期。表中较晚的条目取代了之前的条目。我必须使用开始日期,结束日期和金额查询此表。从这些价值观来看,我需要考虑整体利息金额,并考虑日期跨度的不同利率。

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 Agarwal 的例子来完成,如下所示:

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%)

我遇到的问题是如何将计算与利率表相互关联,以便联接考虑到日期跨度的每个子部分所属的“带”。

非常感谢任何帮助或建议。

2 个答案:

答案 0 :(得分:2)

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

让我们一步一步地介绍,然后将最终解决方案作为一个查询提出。需要几个步骤来解决这个问题。

1)找出我们期望的日期范围包括哪些费率

2)设计一种聪明的方法来选择这些费率

3)以这种方式结合这些日期和费率,以便给予我们累计的利息。


一些初步笔记

由于您的利率示例计算将天数视为最佳分辨率,因此我只使用数据类型 date 而不是 datetime 。如果您需要更精细的分辨率,请告诉我,我可以更新。

我正在使用以下声明的变量

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_begin inter_end 来遇到同样的问题。相反,我们必须使用案例陈述,例如

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