将季度和半年度本金和利息付款转换为每月价值的SQL Server查询

问题描述:

我们使用SQL Server 2014作为主要报告数据库,并且我们有需要一些非常特定的数据操作的报告。我们必须处理的数据是可以以各种类型的系列(即季度,半年,年度等)呈现的本金和利息支付的时间表。为确定月收入,需要将这些本金和利息支付从原始格式重新组织为每月的时间表。以下是原始数据格式的一个例子:将季度和半年度本金和利息付款转换为每月价值的SQL Server查询

原始现金流附表

 
Cashflow_Date  Principal  Interest 
-------------  ---------  -------- 
2015-12-15  0    1000.00 
2016-06-15  0    1000.00 
2016-12-15  10000.00  1000.00 

下面是所需要的格式:

希望的现金流附表

 
Cashflow_Date  Principal  Interest 
-------------  ---------  -------- 
2015-12-15  0    166.667 
2016-01-15  0    166.667 
2016-02-15  0    166.667 
2016-03-15  0    166.667 
2016-04-15  0    166.667 
2016-05-15  0    166.667 
2016-06-15  0    166.667 
2016-07-15  0    166.667 
2016-08-15  0    166.667 
2016-09-15  0    166.667 
2016-10-15  0    166.667 
2016-11-15  0    166.667 
2016-12-15  10000.00  1000.00 

基本上,原始计划付款之间的月份需要与原始支付日期一起退还,并且原始支付需要在原始支付日期之间分解为每月的金额(即, 1000/6 = 2015年12月15日至2016年5月15日期间的166.667)。最后一次付款日期(本案2016-12-15)将保持原样。在整个原始计划中,本金和利息的支付保证不会相同,因此重要的是适当地分配付款。

不幸的是,当前的过程使用游标和游标内的循环(非常糟糕,我知道)来生成所需的结果集。任何人都可以提供任何洞察到基于集合的查询,可能能够更快地产生相同的结果?任何援助将不胜感激。

更新方案

每贴在下面一个问题,如果一个时间的本金或利息支付在原有时间表进行,这些款项也相应划分。例如,如果一次性支付本金是在2016年6月15日作出,那么月收入进度将反映在这样:

边缘情况所需现金流计划

 
Cashflow_Date  Principal  Interest 
-------------  ---------  -------- 
2015-12-15  0    166.667 
2016-01-15  0    166.667 
2016-02-15  0    166.667 
2016-03-15  0    166.667 
2016-04-15  0    166.667 
2016-05-15  0    166.667 
2016-06-15  208.33  166.667 
2016-07-15  208.33  166.667 
2016-08-15  208.33  166.667 
2016-09-15  208.33  166.667 
2016-10-15  208.33  166.667 
2016-11-15  208.33  166.667 
2016-12-15  10000.00  1000.00 
+0

什么时候一次定期支付款项发生,以及如何做,我们的处理主要付款?你能展示这些边缘病例或不规则病例的预期输出吗? – objectNotFound

+0

很高兴。在您指定的情况下,付款需要在原始付款之后的几个月内分摊,直到下一次计划付款。例如,如果在2016年6月15日以1250美元的金额进行本金支付,则该月份和每个月到下一个预定的支付月份将收到原始1250美元的一部分(每月208.33美元的本金支付,直到2016年 - 12-15)。 – user6554159

+0

请问您可以通过编辑您的问题以表格方式表示该问题吗? – objectNotFound

想必你想是这样的:

DECLARE @ TABLE (Cashflow_Date DATE, Principal DECIMAL (10,2), Interest DECIMAL (10,2)); 
INSERT @ VALUES ('2015-12-15', 0, 1000.0), ('2016-06-15', 1250.0, 1000.0), ('2016-12-15', 10000.0, 1000.0); 

SELECT DATEADD(MONTH, n.number, Cashflow_Date) Dates 
    , MAX(Principal)/ISNULL(DATEDIFF(MONTH, Cashflow_Date, nextDate), 1) Principal 
    , MAX(Interest)/ISNULL(DATEDIFF(MONTH, Cashflow_Date, nextDate), 1) Interest 
FROM (
    SELECT t.Cashflow_Date 
     , t.Principal 
     , t.Interest 
     , X.nextDate 
    FROM @ t 
    OUTER APPLY (
     SELECT MIN(Cashflow_Date) 
     FROM @ 
     WHERE Cashflow_Date > t.Cashflow_Date) X(nextDate)) t 
CROSS JOIN (
    SELECT number 
    FROM master..spt_values 
    WHERE type='P') n 
WHERE n.number < ISNULL(DATEDIFF(MONTH, Cashflow_Date, nextDate), 1) 
GROUP BY DATEADD(MONTH, n.number, Cashflow_Date), nextDate, Cashflow_Date 
ORDER BY DATEADD(MONTH, n.number, Cashflow_Date); 

你需要某种形式的理货表的填写的第一个日期和未来日期之间的日期,那么你只需要根据每月的数量以平均本金和利息s在第一次约会和下一次约会之间。

+0

太棒了!谢谢! – user6554159

看看,这将帮助您开始朝着摆脱光标

(不适用于所有场景测试)

/* 
one time setup 

CREATE TABLE [dbo].[Tbl] 
(
    [cf] [date] NOT NULL, 
    [pmt] [int] NOT NULL, 
    [intrest] [int] NOT NULL 
) 
GO 

insert into dbo.Tbl values ('2015-12-15', 0 , 1000) 
insert into dbo.Tbl values ('2015-06-15', 0 , 1000) 
insert into dbo.Tbl values ('2016-12-15', 1000 , 1000) 

*/ 


select Top 13 b.* , intrest/6.0 as int_mnthly, DATEADD(Month,Mnth, cf) as cf_mnthly 
from dbo.Tbl b 
cross join 
( 
    select 1 as Mnth Union ALL 
    select 2 as Mnth Union ALL 
    select 3 as Mnth Union ALL 
    select 4 as Mnth Union ALL 
    select 5 as Mnth Union ALL 
    select 6 as Mnth 
) a 
order by cf_mnthly