距离给定日期和频率最近的日期

时间:2017-01-05 14:22:26

标签: sql-server tsql

我有一个包含开始日期和报告频率的表格(每1个月,2个月等等。粒度为一个月)

我如何创建一个选择,在实际的一天,将返回最近的未来报告的日期?

Example:  
startDate: 15.01.2015, frequency: 3 months  

if today is 03.01.2016 it should return 15.01.2016  
if today is 16.01.2016 it should return 15.04.2016

不能选择存储上次报告的日期。

编辑:请求的样本数据和预期结果:

Actual date is 05.01.2017

StartDate   Freq        Result
02.01.2016  1month      02.02.2017
06.01.2016  1month      06.01.2017
10.03.2016  3months     10.03.2017
01.01.2015  4months     01.05.2017

2 个答案:

答案 0 :(得分:1)

好的,让我们玩数学。

计算从某个StartDate每M个月发生的事件的最近发生?

好吧,首先我计算整数除法和模数的remiander,在Month by Frequency之间,以防MONTH(StartDate)= MONTH(ActualDate)

StartDate - ActualDate % Frequency

然后我需要知道已经提取了多少次报告:

Occurrences = StartDate - ActualDate / Frequency

然后添加一个新的事件并乘以你应该添加到StartDate的月数的频率,当DAY(StartDate)< DAY(ActualDate)。

(Occurrences + 1) * Frequency = Required month

create table #t (StartDate datetime, Freq varchar(10));
insert into #t values
('2016-01-02','1month'),
('2016-01-06','1month'),
('2016-03-10','3months'),
('2015-01-01','4months'),
('2016-01-06','3month');

DECLARE @ActualDate datetime;
SET @ActualDate = '2017-01-05'

SELECT @ActualDate as ActualDate;

;WITH Calc AS
(
    SELECT StartDate, Freq, CAST(LEFT(Freq, 1) AS Int) as intFreq,
           (DATEDIFF(month, StartDate, @ActualDate) / CAST(LEFT(Freq, 1) AS Int)) AS Occurrences,
           (DATEDIFF(month, StartDate, @ActualDate) % CAST(LEFT(Freq, 1) AS Int)) AS restIntDiv,
           DATEPART(day, StartDate) AS StartDay,
           DATEPART(day, @ActualDate) AS ActDay
    FROM #t
)
SELECT StartDate, Freq,
       CASE WHEN restIntDiv = 0 AND ActDay < StartDay
            THEN DATEADD(month, intFreq * (Occurrences), StartDate)
            ELSE DATEADD(month, intFreq * (Occurrences + 1), StartDate)
       END as [NextReport]
FROM Calc;

+---------------------+---------+---------------------+
|      StartDate      |   Freq  | NextReport          |
+---------------------+---------+---------------------+
| 02.01.2016 00:00:00 |  1month | 02.02.2017 00:00:00 |
+---------------------+---------+---------------------+
| 06.01.2016 00:00:00 |  1month | 06.01.2017 00:00:00 |
+---------------------+---------+---------------------+
| 10.03.2016 00:00:00 | 3months | 10.03.2017 00:00:00 |
+---------------------+---------+---------------------+
| 01.01.2015 00:00:00 | 4months | 01.05.2017 00:00:00 |
+---------------------+---------+---------------------+
| 06.01.2016 00:00:00 |  3month | 06.01.2017 00:00:00 |
+---------------------+---------+---------------------+

可在此处查看:http://rextester.com/MBSM51925

答案 1 :(得分:0)

我经常使用TVF创建动态日期/时间范围。 Tally表也可以解决这个问题。 UDF比递归CTE更快(特别是对于更大的集合)并提供更多功能。日期范围,日期分数和增量是参数。

Declare @YourTable table (StartDate date,frequency varchar(25))
Insert Into @YourTable values
('2016-01-02','1month'),
('2016-01-06','1month'),
('2016-03-10','3months'),
('2015-01-01','4months')

Select A.*
      ,NextDate=cast(B.RetVal as date)
 From  @YourTable A
 Cross Apply (
                Select Top 1 * From [dbo].[udf-Range-Date](A.StartDate,'2025-01-15','MM',left(A.frequency,1))
                Where RetVal>=cast(GetDate() as date)
             ) B

返回

StartDate   frequency   NextDate
2016-01-02  1month      2017-02-02
2016-01-06  1month      2017-01-06
2016-03-10  3months     2017-03-10
2015-01-01  4months     2017-05-01

UDF如有兴趣

CREATE FUNCTION [dbo].[udf-Range-Date] (@R1 datetime,@R2 datetime,@Part varchar(10),@Incr int)
Returns Table
Return (
    with cte0(M)   As (Select 1+Case @Part When 'YY' then DateDiff(YY,@R1,@R2)/@Incr When 'QQ' then DateDiff(QQ,@R1,@R2)/@Incr When 'MM' then DateDiff(MM,@R1,@R2)/@Incr When 'WK' then DateDiff(WK,@R1,@R2)/@Incr When 'DD' then DateDiff(DD,@R1,@R2)/@Incr When 'HH' then DateDiff(HH,@R1,@R2)/@Incr When 'MI' then DateDiff(MI,@R1,@R2)/@Incr When 'SS' then DateDiff(SS,@R1,@R2)/@Incr End),
         cte1(N)   As (Select 1 From (Values(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) N(N)),
         cte2(N)   As (Select Top (Select M from cte0) Row_Number() over (Order By (Select NULL)) From cte1 a, cte1 b, cte1 c, cte1 d, cte1 e, cte1 f, cte1 g, cte1 h ),
         cte3(N,D) As (Select 0,@R1 Union All Select N,Case @Part When 'YY' then DateAdd(YY, N*@Incr, @R1) When 'QQ' then DateAdd(QQ, N*@Incr, @R1) When 'MM' then DateAdd(MM, N*@Incr, @R1) When 'WK' then DateAdd(WK, N*@Incr, @R1) When 'DD' then DateAdd(DD, N*@Incr, @R1) When 'HH' then DateAdd(HH, N*@Incr, @R1) When 'MI' then DateAdd(MI, N*@Incr, @R1) When 'SS' then DateAdd(SS, N*@Incr, @R1) End From cte2 )

    Select RetSeq = N+1
          ,RetVal = D 
     From  cte3,cte0 
     Where D<=@R2
)
/*
Max 100 million observations -- Date Parts YY QQ MM WK DD HH MI SS
Syntax:
Select * from [dbo].[udf-Range-Date]('2016-10-01','2020-10-01','YY',1) 
Select * from [dbo].[udf-Range-Date]('2016-01-01','2017-01-01','MM',1) 
*/