【问题标题】:Get an interval of dates from a range of dates从日期范围中获取日期间隔
【发布时间】:2020-09-16 15:02:17
【问题描述】:

我有两个日期 21/10/2019 和 26/6/2031,从这两个日期开始,我需要从第一个日期开始的三个月间隔的日期列表,如下所示:

22/10/2019 | 21/01/2020
22/01/2020 | 21/04/2020
22/04/2020 | 21/07/2020
22/07/2020 | 21/10/2020 
...
22/01/2031 | 21/04/2031
22/04/2031 | 26/06/2031

我尝试使用ROW_NUMBER()DENSE_RANK()LAG() 将两个日期之间的完整日期列表分组,但我似乎无法弄清楚。我想我可能需要以某种方式对其进行分区,但我做不到。

如果你不明白,请告诉我。我对此很陌生:)

【问题讨论】:

  • 您接受的答案与您发布的预期输出不匹配。接受的答案有一个 Start_Date 开始于本月 21 日并结束于本月 20 日,但您非常明确地发布了预期输出,其中 Start_Dates 都在本月 22 日和 End_Date 结束于每个月的 21 日一个月的回报。哪个是对的?您的预期输出或您接受的答案?
  • 我接受了答案,因为代码可以正常工作,我只是修改了它以获得我需要的日期。我不知道递归查询,所以这对我很有帮助:-)
  • 好的,谢谢。当涉及到内存读取和 CPU 时,即使对于相对较小的东西,增量递归 CTE 也会对系统造成很大的负担。您是否对不同的方式感兴趣?

标签: sql sql-server date datetime recursive-query


【解决方案1】:

您可以使用递归查询:

with cte (dt, end_dt) as (
    select @start_dt, @end_dt
    union all
    select dateadd(month, 3, dt), end_dt from cte where dt < end_dt
)
select dt, 
    case when dateadd(month, 3, dt) < end_dt 
        then dateadd(day, -1, dateadd(month, 3, dt)) 
        else end_dt 
    end as end_dt
from cte
order by dt;

如果您需要生成超过 100 个季度,则需要在查询的最后添加 option (maxrecursion 0)

Demo on DB Fiddle

【讨论】:

  • 您可能想再次查看结果。您在原始帖子中指定每行的开始日期都应该在第 22 天,同一行的结束日期应该在第 21 天(系列中的最后一天除外),并且此代码没有这样做。
  • 我接受了答案,因为代码可以正常工作,我只是修改了它以获得我需要的日期。我不知道递归查询,所以这对我很有帮助:-)
  • 好的,谢谢。当涉及到内存读取和 CPU 时,即使对于相对较小的东西,增量递归 CTE 也会对系统造成很大的负担。您是否对不同的方式感兴趣?
【解决方案2】:

这也可以使用“计数”或基于数字的方法来完成。 tally_cte 的上限是 12^5=248,832(超过递归可以产生的值,可以根据需要增加)。

declare
  @start_dt         datetime='2019-10-21',
  @end_dt           datetime='2031-06-26'

;with
n(n) as (select * from (values (1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12)) v(n)),
tally_cte(n) as (
     select row_number() over (order by (select null)) 
     from n n1 cross join n n2 cross join n n3 cross join n n4 cross join n n5)
select t.*, cast(dt.dt as date) start_dt, cast(dateadd(MONTH, 3, dt.dt) as date) end_dt
from tally_cte t
     cross apply  (select dateadd(month, (n-1)*3, @start_dt) dt) dt 
where n<datediff(month, @start_dt, @end_dt)/3;

N   start_dt    end_dt
1   2019-10-21  2020-01-21
2   2020-01-21  2020-04-21
3   2020-04-21  2020-07-21
...
45  2030-10-21  2031-01-21

【讨论】:

  • 正确的想法,但是您没有将 start_dt 设置为每个月的 22 日,因为 OP 说他希望凭借他的样本输出,也没有提供最终日期(您错过了最后两个系列的行)。
猜你喜欢
  • 1970-01-01
  • 2013-03-04
  • 2021-10-25
  • 1970-01-01
  • 2022-01-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-06-05
相关资源
最近更新 更多