合并重叠的日期间隔

leo*_*nfo 15 sql t-sql sql-server

有没有更好的方法来合并重叠的日期间隔?
我提出的解决方案非常简单,现在我想知道其他人是否能更好地了解如何做到这一点.

/***** DATA EXAMPLE *****/
DECLARE @T TABLE (d1 DATETIME, d2 DATETIME)
INSERT INTO @T (d1, d2)
        SELECT '2010-01-01','2010-03-31' UNION SELECT '2010-04-01','2010-05-31' 
  UNION SELECT '2010-06-15','2010-06-25' UNION SELECT '2010-06-26','2010-07-10' 
  UNION SELECT '2010-08-01','2010-08-05' UNION SELECT '2010-08-01','2010-08-09' 
  UNION SELECT '2010-08-02','2010-08-07' UNION SELECT '2010-08-08','2010-08-08' 
  UNION SELECT '2010-08-09','2010-08-12' UNION SELECT '2010-07-04','2010-08-16' 
  UNION SELECT '2010-11-01','2010-12-31' UNION SELECT '2010-03-01','2010-06-13' 

/***** INTERVAL ANALYSIS *****/
WHILE (1=1)  BEGIN
  UPDATE t1 SET t1.d2 = t2.d2
  FROM @T AS t1 INNER JOIN @T AS t2 ON 
            DATEADD(day, 1, t1.d2) BETWEEN t2.d1 AND t2.d2 
  IF @@ROWCOUNT = 0 BREAK
END

/***** RESULT *****/
SELECT StartDate = MIN(d1) , EndDate = d2
FROM @T
GROUP BY d2
ORDER BY StartDate, EndDate

/***** OUTPUT *****/
/*****
StartDate   EndDate
2010-01-01  2010-06-13 
2010-06-15  2010-08-16 
2010-11-01  2010-12-31 
*****/
Run Code Online (Sandbox Code Playgroud)

小智 20

我正在寻找相同的解决方案,并在Combine重叠日期时间上发现了这篇文章,以返回单个重叠范围记录.

包装日期间隔还有另一个主题.

我用各种日期范围测试了这个,包括这里列出的日期范围,并且它每次都能正常工作.


SELECT 
       s1.StartDate,
       --t1.EndDate 
       MIN(t1.EndDate) AS EndDate
FROM @T s1 
INNER JOIN @T t1 ON s1.StartDate <= t1.EndDate
  AND NOT EXISTS(SELECT * FROM @T t2 
                 WHERE t1.EndDate >= t2.StartDate AND t1.EndDate < t2.EndDate) 
WHERE NOT EXISTS(SELECT * FROM @T s2 
                 WHERE s1.StartDate > s2.StartDate AND s1.StartDate <= s2.EndDate) 
GROUP BY s1.StartDate 
ORDER BY s1.StartDate 
Run Code Online (Sandbox Code Playgroud)

结果是:

StartDate  | EndDate
2010-01-01 | 2010-06-13
2010-06-15 | 2010-06-25
2010-06-26 | 2010-08-16
2010-11-01 | 2010-12-31
Run Code Online (Sandbox Code Playgroud)

  • 请注意,截至 2017 年 12 月 19 日,/http://www.sqlmag.com/blog/puzzled-by-t-sql-blog-15/tsql/packing-date-intervals-136831 URL 为 404。WayBack Machine 给出了一个历史参考:https://web.archive.org/web/20130403002905/http://www.sqlmag.com/blog/puzzled-by-t-sql-blog-15/tsql/packing-日期间隔-136831。 (2认同)

Mar*_*ith 7

您在2010年问过这个问题,但没有指定任何特定版本.

SQL Server 2012+上的人们的答案

WITH T1
     AS (SELECT *,
                MAX(d2) OVER (ORDER BY d1) AS max_d2_so_far
         FROM   @T),
     T2
     AS (SELECT *,
                CASE
                  WHEN d1 <= DATEADD(DAY, 1, LAG(max_d2_so_far) OVER (ORDER BY d1))
                    THEN 0
                  ELSE 1
                END AS range_start
         FROM   T1),
     T3
     AS (SELECT *,
                SUM(range_start) OVER (ORDER BY d1) AS range_group
         FROM   T2)
SELECT range_group,
       MIN(d1) AS d1,
       MAX(d2) AS d2
FROM   T3
GROUP  BY range_group 
Run Code Online (Sandbox Code Playgroud)

哪个回报

+-------------+------------+------------+
| range_group |     d1     |     d2     |
+-------------+------------+------------+
|           1 | 2010-01-01 | 2010-06-13 |
|           2 | 2010-06-15 | 2010-08-16 |
|           3 | 2010-11-01 | 2010-12-31 |
+-------------+------------+------------+
Run Code Online (Sandbox Code Playgroud)

DATEADD(DAY, 1之所以使用,是因为您想要的结果显示您希望将一个句点结束2010-06-25为一个开始2010-06-26.对于其他用例,可能需要调整.


Tho*_*mas 0

在此解决方案中,我创建了一个临时日历表,用于存储一定范围内每天的值。这种类型的表可以设为静态。另外,我只存储了 400 个从 2009 年 12 月 31 日开始的奇怪日期。显然,如果您的日期跨越更大的范围,您将需要更多的值。

此外,该解决方案仅适用于 SQL Server 2005+,因为我使用的是 CTE。

With Calendar As
    (
    Select DateAdd(d, ROW_NUMBER() OVER ( ORDER BY s1.object_id ), '1900-01-01') As [Date]
    From sys.columns as s1
        Cross Join sys.columns as s2
    )
    , StopDates As
    (
    Select C.[Date]
    From Calendar As C
        Left Join @T As T
            On C.[Date] Between T.d1 And T.d2
    Where C.[Date] >= ( Select Min(T2.d1) From @T As T2 )
        And C.[Date] <= ( Select Max(T2.d2) From @T As T2 )
        And T.d1 Is Null
    )
    , StopDatesInUse As
    (
    Select D1.[Date]
    From StopDates As D1
        Left Join StopDates As D2
            On D1.[Date] = DateAdd(d,1,D2.Date)
    Where D2.[Date] Is Null
    )
    , DataWithEariestStopDate As 
    (
    Select *
    , (Select Min(SD2.[Date])
        From StopDatesInUse As SD2
        Where T.d2 < SD2.[Date] ) As StopDate
    From @T As T
    )
Select Min(d1), Max(d2)
From DataWithEariestStopDate
Group By StopDate
Order By Min(d1)
Run Code Online (Sandbox Code Playgroud)

编辑使用 2009 年日期的问题与最终查询无关。问题是日历表不够大。我在 2009 年 12 月 31 日启动了日历表。我从 1900 年 1 月 1 日开始对其进行了修改。