SQL:从一组日期范围

时间:2016-06-22 21:16:33

标签: sql sql-server date common-table-expression

我正在尝试为我的问题找到合适的标题,到目前为止,它已经尝试了30分钟... :) 到目前为止,我有以下示例

DateFrom    DateTo   Amount 
2015/01/01 2015/08/31 1$ 
2015/01/01 2015/12/31 3$
2015/08/01 2015/12/31 7$
  1. 对于第一行,我们得到0.125 $ /月(1 $ / 8个月)
  2. 对于第二行,我们得到0.25美元/月(3 $ / 12个月)
  3. 对于3d线,我们得到1.4美元/月(7 $ / 5个月)
  4. 考虑到上述情况,我们希望创建一组新的日期范围,以便获得金额的总和。类似下面的结果:

    DateFrom    DateTo          Amount
    2015/01/01 2015/07/31    (0.125$+0.25$)*7 =2.625$
    2015/08/01 2015/08/31    (1.4$+0.125$+0.25$)*1  =1.775$
    2015/09/01 2015/12/31    (1.4$+0.25$)*4   =6.6$
    

    上述总和为11美元,就像原始数据一样。我们想要的结果实际上是每个唯一的日期范围组的金额之和。

    这可以通过SQL实现吗?

1 个答案:

答案 0 :(得分:0)

解决方案不是很优雅,可能有一些不必要的部分,但它产生了正确的结果:

SELECT * INTO tbl_Periods
FROM ( VALUES
('2015/01/01','2015/08/31',1),
('2015/01/01','2015/12/31',3),
('2015/08/01','2015/12/31',7)) as x(DateFrom,DateTo,Amount);
GO
;WITH 
  Pass0 as (select 1 as C union all select 1),
  Pass1 as (select 1 as C from Pass0 as A, Pass0 as B),
  Pass2 as (select 1 as C from Pass1 as A, Pass1 as B),
  Pass3 as (select 1 as C from Pass2 as A, Pass2 as B)
, CTE1 as (
    SELECT *,  Amount / DateDiff(MONTH, DateFrom, DATEADD(day, 1 ,DateTo)) as MRate
    FROM tbl_Periods
)
, CTEM as (SELECT MIN(DateFrom) as MinDate,  DATEADD(day, 1, MAX(DateTo) ) as MaxDate
    ,  DateDiff(MONTH, MIN(DateFrom), DATEADD(day, 1, MAX(DateTo) )) as NPeriods FROM tbl_Periods)
, CTEP as (
    SELECT TOP ((SELECT NPeriods FROM CTEM)) ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) as RNumber 
    FROM Pass3 as p
    )
, PeriodList as (
    SELECT DateAdd(month, RNumber-1,MinDate) as PeriodStarts, DateAdd(month, RNumber,DATEADD(day,-1,MinDate)) as PeriodEnds
    FROM CTEM as m CROSS JOIN CTEP as p)
, MPerriods as (
    SELECT * 
    FROM CTE1 as c 
    INNER JOIN PeriodList as l
    ON l.PeriodStarts between c.DateFrom and c.DateTo)
, CPeriods as (
    SELECT PeriodStarts, PeriodEnds, SUM(MRate) as MRate
        , RNK  = RANK() OVER(ORDER BY PeriodStarts)
            - RANK() OVER(PARTITION BY SUM(MRate) ORDER BY PeriodStarts)
    FROM MPerriods
    GROUP BY PeriodStarts, PeriodEnds
) 
SELECT MIN(PeriodStarts) as PeriodStarts, MAX(PeriodEnds) as PeriodEnds, COUNT(*) * MRate
FROM CPeriods
GROUP BY MRate, RNK
ORDER BY 1;
GO

enter image description here