我有一张表存储利率,每个利率都有一个适用的开始日期。表中较晚的条目取代了之前的条目。我必须使用开始日期,结束日期和金额查询此表。从这些价值观来看,我需要考虑整体利息金额,并考虑日期跨度的不同利率。
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%)
我遇到的问题是如何将计算与利率表相互关联,以便联接考虑到日期跨度的每个子部分所属的“带”。
非常感谢任何帮助或建议。
答案 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
<强>返回强>