【问题标题】:nearest future date from given date and frequency距给定日期和频率最近的未来日期
【发布时间】:2017-01-05 14:22:25
【问题描述】:

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

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

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

【问题讨论】:

  • 你能展示样本数据和预期结果吗
  • @TheGameiswar 示例数据已添加,如果还不够请告诉我
  • 未来有多少报告?
  • 提示:如果您使用 ISO 日期字符串,会更容易混淆:20150102 是 2015 年 1 月 2 日,无论本地化如何。
  • @HABO 下次我会记住这一点,谢谢

标签: sql-server tsql


【解决方案1】:

好吧,来玩数学吧。

计算从某个 StartDate 开始每 M 个月发生一次的事件的最近发生时间?

嗯,首先我计算整数除法和模数的余数,按频率计算,以防万一 MONTH(StartDate) = MONTH(ActualDate)

StartDate - ActualDate % Frequency

那么我需要知道报告被获取了多少次:

Occurrences = StartDate - ActualDate / Frequency

然后添加一个新的事件并乘以频率,您将得到应添加到 StartDate 的月数,注意当 DAY(StartDate)

(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

【讨论】:

  • 不清楚您的回答如何将问题中的StartDate 考虑在内。
  • 原来的问题是:给定开始日期 2015 年1 月15 日,间隔三个月,当前日期之后的下一个事件是什么。您的代码不会出现计算下一次发生是在当前月份、下个月还是下个月根据开始日期。从今天开始不太可能是三个月,除非今天是第 15 天并且恰好是自开始日期起三个月的倍数。
  • 好吧,或许不用cte也能完成,不过这样更清楚..
  • 我想就是这样.. 我可以在星期一正确测试它,但这看起来很有希望 - 所以非常感谢:)
  • @McNets 只是想我会让你知道——它确实工作得很好。 :)
【解决方案2】:

我经常使用 TVF 来创建动态日期/时间范围。 Tally 表也可以解决问题。 UDF 比递归 CTE 更快(尤其是对于较大的集合)并提供更多功能。 Date Range、DatePart 和 Increments 是参数。

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) 
*/

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多