处理重叠的时间段

问题描述 投票:0回答:1

我有 4 个不同的时间段,

a
b
c
d

  • a
    b
  • 重叠
  • b
    c
  • 重叠
  • a
    c
    不重叠
  • d
    不与
    a
    b
    c
  • 重叠

但是,我希望能够将这些全部链接到一个时间段中,无论有多少个不同的时间段可能链接在一起。另一个挑战是我有其他不重叠的时间段,所以我不能简单地对所有数据进行

MIN()
MAX()

以下是我目前的尝试

DROP TABLE IF EXISTS #test
CREATE TABLE 
    #test (id VARCHAR, start_time DATETIME2, end_time DATETIME2)
INSERT INTO
    #test
VALUES 
    ('a', '2024-08-03 12:35:37', '2024-08-07 12:36:42'),
    ('b', '2024-08-06 18:35:53', '2024-08-10 13:12:50'),
    ('c', '2024-08-09 12:40:13', '2024-08-11 00:00:00'),
    ('d', '2024-01-09 12:40:13', '2024-02-11 00:00:00');


WITH overlaps AS (
    SELECT
        a.id,
        a.start_time AS orig_start,
        a.end_time AS orig_end,
        LEAST(a.start_time, b.start_time) AS overlap_start,
        GREATEST(a.end_time, b.end_time) AS overlap_end
    FROM
        #test AS a
    LEFT JOIN
        #test AS b
            ON b.id != a.id -- Don't overlap with self
            AND GREATEST(a.start_time, b.start_time) < LEAST(a.end_time, b.end_time) -- catch any overlap time periods
),
merged AS (
    SELECT
        a.id,
        a.orig_start,
        a.orig_end,
        MIN(overlap_start) AS merged_start,
        MAX(overlap_end) AS merged_end
    FROM
        overlaps AS a
    GROUP BY
        a.id,
        a.orig_start,
        a.orig_end
)
SELECT DISTINCT
    id,
    merged_start,
    merged_end
FROM
    merged
ORDER BY
    merged_start;

期望的最终目标将是这样的表格:

DROP TABLE IF EXISTS #desired
CREATE TABLE 
    #desired (rn INT, start_time DATETIME2, end_time DATETIME2)
INSERT INTO
    #desired
VALUES 
    (1, '2024-01-09 12:40:13', '2024-02-11 00:00:00'),
    (2, '2024-08-03 12:35:37', '2024-08-11 00:00:00');
SELECT
    *
FROM
    #desired;
sql sql-server datetime
1个回答
0
投票

这是一种方法,它与您的尝试没有什么不同,只是最后一步必须停止按

id
分组:

WITH AllPossibleOverlaps AS 
(
  SELECT t1.id,
       s = MIN(t2.start_time), 
       e = MAX(t2.end_time) 
    FROM      #test AS t1 
   INNER JOIN #test AS t2
      ON t2.end_time >= t1.start_time
     AND t2.start_time <= t1.end_time
   GROUP BY t1.id
),
OverlapsById AS 
(
  SELECT x.id,
         start_time = MIN(x2.s), 
         end_time   = MAX(x2.e) 
    FROM      AllPossibleOverlaps AS x
   INNER JOIN AllPossibleOverlaps AS x2
      ON x.e >= x2.s 
     AND x.s <= x2.e
   GROUP BY x.id
)
SELECT rn = ROW_NUMBER() OVER (ORDER BY start_time), 
       start_time,
       end_time 
   FROM OverlapsById
  GROUP BY start_time, 
           end_time
  ORDER BY rn;
© www.soinside.com 2019 - 2024. All rights reserved.