我有一个包含开始日期和报告频率的表格(每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
答案 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 |
+---------------------+---------+---------------------+
答案 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)
*/