查找时间间隔的簇
Find clusters of time intervals
我有一个包含多个条目的 table。一项由开始日期时间和结束日期时间组成。
我想通过以下方式查找条目集群:
如果一个条目在前一个条目结束之前开始,那么两者都是集群的一部分。
这是某种重叠问题。
示例:
id start end
1 2007-04-11 15:34:02 2007-05-11 13:09:01
2 2007-06-13 15:42:39 2009-07-21 11:30:00
3 2007-11-26 14:30:02 2007-12-11 14:09:07
4 2008-02-14 08:52:11 2010-02-23 16:00:00
我想要
的输出
id start end
1 2007-04-11 15:34:02 2007-05-11 13:09:01
2-4 2007-06-13 15:42:39 2010-02-23 16:00:00
我有一个解决方案,可以开始排序,然后使用行号和 lag/lead 等进行一些计算。
问题是第 4 行确实紧接在第 2 行之后的特殊情况,所以我不认识它...
这里sql有好的解决办法吗?也许我遗漏了什么?
试试这个...
select a.id ,a.start,a.end,b.id,b.start,b.end
from tab a
cross join tab b
where a.start between b.start and b.end
order by a.start, a.end
我们必须将每一行与所有其他行进行检查,就像使用循环和内循环一样。为此,我们进行交叉连接。
然后我们将使用 BETWEEN AND 运算符检查重叠
要回答这个问题,您需要确定开始新组的时间。然后,在每次开始之前,统计这样的开始次数来定义一个组——并按这个值聚合。
假设你没有重复的时间,这应该可以设置标志:
select e.*,
(case when not exists (select 1
from entries e2
where e2.start < e.start and e2.end > e.start
)
then 1 else 0
end) as BeginsIsland
from entries e;
下面接着做累加和聚合,假设SQL Server 2012+(这很容易适配到更早的版本,但这样更容易编码):
with e as (
select e.*,
(case when not exists (select 1
from entries e2
where e2.start < e.start and e2.end > e.start
)
then 1 else 0
end) as BeginIslandFlag
from entries e
)
select (case when min(id) = max(id) then cast(max(id) as varchar(255))
else cast(min(id) as varchar(255)) + '-' + cast(max(id) as varchar(255))
end) as ids,
min(start) as start, max(end) as end
from (select e.* sum(BeginIslandFlag) over (order by start) as grp
from e
) e
group by grp;
好的,这是一些递归 cte 的解决方案:
CREATE TABLE t
(
id INT,
s DATE,
e DATE
);
INSERT INTO t
VALUES (1, '20070411', '20070511'),
(2, '20070613', '20090721'),
(3, '20071126', '20071211'),
(4, '20080214', '20100223');
WITH cte AS (
SELECT id, s, e, id AS rid, s AS rs, e AS re
FROM t
WHERE NOT EXISTS(
SELECT *
FROM t ti
WHERE t.s > ti.s
AND t.s < ti.e
)
UNION ALL
SELECT t.*, c.rid, c.rs,
CASE
WHEN t.e > c.re THEN t.e
ELSE c.re
END
FROM t
JOIN cte c ON t.s > c.s AND t.s < c.e
)
SELECT min(id) minid,
max(id) maxid,
min(rs) startdate,
max(re) enddate
FROM cte
GROUP BY rid
输出:
minid maxid startdate enddate
1 1 2007-04-11 2007-05-11
2 4 2007-06-13 2010-02-23
我有一个包含多个条目的 table。一项由开始日期时间和结束日期时间组成。
我想通过以下方式查找条目集群:
如果一个条目在前一个条目结束之前开始,那么两者都是集群的一部分。 这是某种重叠问题。
示例:
id start end
1 2007-04-11 15:34:02 2007-05-11 13:09:01
2 2007-06-13 15:42:39 2009-07-21 11:30:00
3 2007-11-26 14:30:02 2007-12-11 14:09:07
4 2008-02-14 08:52:11 2010-02-23 16:00:00
我想要
的输出id start end
1 2007-04-11 15:34:02 2007-05-11 13:09:01
2-4 2007-06-13 15:42:39 2010-02-23 16:00:00
我有一个解决方案,可以开始排序,然后使用行号和 lag/lead 等进行一些计算。 问题是第 4 行确实紧接在第 2 行之后的特殊情况,所以我不认识它...
这里sql有好的解决办法吗?也许我遗漏了什么?
试试这个...
select a.id ,a.start,a.end,b.id,b.start,b.end
from tab a
cross join tab b
where a.start between b.start and b.end
order by a.start, a.end
我们必须将每一行与所有其他行进行检查,就像使用循环和内循环一样。为此,我们进行交叉连接。
然后我们将使用 BETWEEN AND 运算符检查重叠
要回答这个问题,您需要确定开始新组的时间。然后,在每次开始之前,统计这样的开始次数来定义一个组——并按这个值聚合。
假设你没有重复的时间,这应该可以设置标志:
select e.*,
(case when not exists (select 1
from entries e2
where e2.start < e.start and e2.end > e.start
)
then 1 else 0
end) as BeginsIsland
from entries e;
下面接着做累加和聚合,假设SQL Server 2012+(这很容易适配到更早的版本,但这样更容易编码):
with e as (
select e.*,
(case when not exists (select 1
from entries e2
where e2.start < e.start and e2.end > e.start
)
then 1 else 0
end) as BeginIslandFlag
from entries e
)
select (case when min(id) = max(id) then cast(max(id) as varchar(255))
else cast(min(id) as varchar(255)) + '-' + cast(max(id) as varchar(255))
end) as ids,
min(start) as start, max(end) as end
from (select e.* sum(BeginIslandFlag) over (order by start) as grp
from e
) e
group by grp;
好的,这是一些递归 cte 的解决方案:
CREATE TABLE t
(
id INT,
s DATE,
e DATE
);
INSERT INTO t
VALUES (1, '20070411', '20070511'),
(2, '20070613', '20090721'),
(3, '20071126', '20071211'),
(4, '20080214', '20100223');
WITH cte AS (
SELECT id, s, e, id AS rid, s AS rs, e AS re
FROM t
WHERE NOT EXISTS(
SELECT *
FROM t ti
WHERE t.s > ti.s
AND t.s < ti.e
)
UNION ALL
SELECT t.*, c.rid, c.rs,
CASE
WHEN t.e > c.re THEN t.e
ELSE c.re
END
FROM t
JOIN cte c ON t.s > c.s AND t.s < c.e
)
SELECT min(id) minid,
max(id) maxid,
min(rs) startdate,
max(re) enddate
FROM cte
GROUP BY rid
输出:
minid maxid startdate enddate
1 1 2007-04-11 2007-05-11
2 4 2007-06-13 2010-02-23