sql-server 使用开始日期和结束日期将日期分割为间隔

yuvru6vn  于 2022-10-31  发布在  其他
关注(0)|答案(4)|浏览(290)

我有一个场景,我需要将给定的日期范围拆分为每月间隔。
例如,输入如下所示:

  1. StartDate EndDate
  2. 2018-01-21 2018-01-29
  3. 2018-01-30 2018-02-23
  4. 2018-02-24 2018-03-31
  5. 2018-04-01 2018-08-16
  6. 2018-08-17 2018-12-31

预期输出如下:

  1. StartDate EndDate
  2. 2018-01-21 2018-01-29
  3. 2018-01-30 2018-01-31
  4. 2018-02-01 2018-02-23
  5. 2018-02-24 2018-02-28
  6. 2018-03-01 2018-03-31
  7. 2018-04-01 2018-04-30
  8. 2018-05-01 2018-05-31
  9. 2018-06-01 2018-06-30
  10. 2018-07-01 2018-07-31
  11. 2018-08-01 2018-08-16
  12. 2018-08-17 2018-08-31
  13. 2018-09-01 2018-09-30
  14. 2018-10-01 2018-10-31
  15. 2018-11-01 2018-11-30
  16. 2018-12-01 2018-12-31

以下是示例数据。

  1. CREATE TABLE #Dates
  2. (
  3. StartDate DATE,
  4. EndDate DATE
  5. );
  6. INSERT INTO #Dates
  7. (
  8. StartDate,
  9. EndDate
  10. )
  11. VALUES
  12. ('2018-01-21', '2018-01-29'),
  13. ('2018-01-30', '2018-02-23'),
  14. ('2018-02-24', '2018-03-31'),
  15. ('2018-04-01', '2018-08-16'),
  16. ('2018-08-17', '2018-12-31');
xeufq47z

xeufq47z1#

可以使用递归CTE。基本思想是从第一个日期2018-01-21开始,建立一个到最后一个日期2018-12-31为止的所有月份的开始和结束日期的列表。然后与数据进行内部连接,并在必要时钳住日期。

  1. DECLARE @Dates TABLE (StartDate DATE, EndDate DATE);
  2. INSERT INTO @Dates (StartDate, EndDate) VALUES
  3. ('2018-01-21', '2018-01-29'),
  4. ('2018-01-30', '2018-02-23'),
  5. ('2018-02-24', '2018-03-31'),
  6. ('2018-04-01', '2018-08-16'),
  7. ('2018-08-17', '2018-12-31');
  8. WITH minmax AS (
  9. -- clamp min(start date) to 1st day of that month
  10. SELECT DATEADD(MONTH, DATEDIFF(MONTH, CAST('00010101' AS DATE), MIN(StartDate)), CAST('00010101' AS DATE)) AS mindate, MAX(EndDate) AS maxdate
  11. FROM @Dates
  12. ), months AS (
  13. -- calculate first and last day of each month
  14. -- e.g. for February 2018 it'll return 2018-02-01 and 2018-02-28
  15. SELECT mindate AS date01, DATEADD(DAY, -1, DATEADD(MONTH, 1, mindate)) AS date31, maxdate
  16. FROM minmax
  17. UNION ALL
  18. SELECT DATEADD(MONTH, 1, prev.date01), DATEADD(DAY, -1, DATEADD(MONTH, 2, prev.date01)), maxdate
  19. FROM months AS prev
  20. WHERE prev.date31 < maxdate
  21. )
  22. SELECT
  23. -- clamp start and end date to first and last day of corresponding month
  24. CASE WHEN StartDate < date01 THEN date01 ELSE StartDate END,
  25. CASE WHEN EndDate > date31 THEN date31 ELSE EndDate END
  26. FROM months
  27. INNER JOIN @Dates ON date31 >= StartDate AND EndDate >= date01

如果rCTE不是一个选项,你总是可以JOIN一个数字表或日期表(上面的想法仍然适用)。

展开查看全部
zlwx9yxi

zlwx9yxi2#

您可以交叉应用Master..spt_values表,以便为StartDate和EndDate之间的每个月获取一行。

  1. SELECT *
  2. into #dates
  3. FROM (values
  4. ('2018-01-21', '2018-01-29')
  5. ,('2018-01-30', '2018-02-23')
  6. ,('2018-02-24', '2018-03-31')
  7. ,('2018-04-01', '2018-08-16')
  8. ,('2018-08-17', '2018-12-31')
  9. )d(StartDate , EndDate)
  10. SELECT
  11. SplitStart as StartDate
  12. ,case when enddate < SplitEnd then enddate else SplitEnd end as EndDate
  13. FROM #dates d
  14. cross apply (
  15. SELECT
  16. cast(dateadd(mm, number, dateadd(dd, (-datepart(dd, d.startdate) +1) * isnull((number / nullif(number, 0)), 0), d.startdate)) as date) as SplitStart
  17. ,cast(dateadd(dd, -datepart(dd, dateadd(mm, number+1, startdate)), dateadd(mm, number+1, startdate)) as date) as SplitEnd
  18. FROM
  19. master..spt_values
  20. where type = 'p'
  21. and number between 0 and (((year(enddate) - year(startdate)) * 12) + month(enddate) - month(startdate))
  22. ) s
  23. drop table #dates
展开查看全部
t9eec4r0

t9eec4r03#

下面的方法也应该有效
1.首先,我将startdates和enddates放入cte-block数据中的一列。
1.在som_eom块中,我为所有12个月创建了start_of_month和end_of_month。
1.我将步骤1和步骤2合并到curated_set中
1.我创建了curated_set,它是按日期列排序的
1.最后,我拒绝不需要的记录,在我的过滤器子句不在('som','StartDate')

  1. with data
  2. as (select *
  3. from dates
  4. unpivot(x for y in(startdate,enddate))t
  5. )
  6. ,som_eom
  7. as (select top 12
  8. cast('2018-'+cast(row_number() over(order by (select null)) as varchar(2))+'-01' as date) as som
  9. ,dateadd(dd
  10. ,-1
  11. ,dateadd(mm
  12. ,1
  13. ,cast('2018-'+cast(row_number() over(order by (select null)) as varchar(2))+'-01' as date)
  14. )
  15. ) as eom
  16. from information_schema.tables
  17. )
  18. ,curated_set
  19. as(select *
  20. from data
  21. union all
  22. select *
  23. from som_eom
  24. unpivot(x for y in(som,eom))t
  25. )
  26. ,curated_data
  27. as(select x
  28. ,y
  29. ,lag(x) over(order by x) as prev_val
  30. from curated_set
  31. )
  32. select prev_val as st_dt,x as end_dt
  33. ,y
  34. from curated_Data
  35. where y not in('som','StartDate')
展开查看全部
jei2mxaa

jei2mxaa4#

从初始的StartDate开始计算月末,或者如果月末在同一个月内,则直接使用EndDate。使用新计算的EndDate+1作为递归的StartDate,然后重复计算。

  1. WITH cte AS
  2. ( SELECT StartDate, -- initial start date
  3. CASE WHEN EndDate < DATEADD(DAY,-1,DATEADD(MONTH, DATEDIFF(MONTH,0,StartDate)+1,0))
  4. THEN EndDate
  5. ELSE DATEADD(DAY,-1,DATEADD(MONTH, DATEDIFF(MONTH,0,StartDate)+1,0))
  6. END AS newEnd, -- LEAST(end of current month, EndDate)
  7. EndDate
  8. FROM #Dates
  9. UNION ALL
  10. SELECT dateadd(DAY,1,newEnd), -- previous end + 1 day, i.e. 1st of current month
  11. CASE WHEN EndDate <= DATEADD(DAY,-1,DATEADD(MONTH, DATEDIFF(MONTH,0,StartDate)+2,0))
  12. THEN EndDate
  13. ELSE DATEADD(DAY,-1,DATEADD(MONTH, DATEDIFF(MONTH,0,StartDate)+2,0))
  14. END, -- LEAST(end of next month, EndDate)
  15. EndDate
  16. FROM cte
  17. WHERE newEnd < EndDate
  18. )
  19. SELECT StartDate, newEnd
  20. FROM cte
展开查看全部

相关问题