获取PostgreSQL中多个时间范围的所有可能交集
Get all possible intersections of multiple time ranges in PostgreSQL
我正在尝试解决与此类似的问题Find all intersections of all sets of ranges in PostgreSQL
不同之处在于,在这个线程中,它获取 所有 范围重叠的范围,而在我的用例中,我所有可能的重叠:
考虑4个范围如
[2018-01-01, 2018-01-15]
[2018-01-01, 2018-01-02]
[2018-01-07, 2018-01-20]
[2018-01-12, 2018-01-30]
创建这样的时间轴
A [==============]
B [=]
C [=============]
D [===============]
我想获取所有发生的重叠,所以:
{ entities: [A, B], period: [2018-01-01, 2018-01-02] }
{ entities: [A, C], period: [2018-01-07, 2018-01-11] }
{ entities: [A, C, D], period: [2018-01-12, 2018-01-15] }
{ entities: [C, D], period: [2018-01-15, 2018-01-20] }
另一件事,在结果中我需要同一组中最可能的重叠,这解释了为什么没有 A, D
重叠。
我已经在同一时期得到 A, C, D
并且没有只有 A, D
重叠的时期,而有一个 A, C
例如。
我设法使来自其他线程的查询与我的设置/表一起工作,但我不确定是否理解所有这些,尤其是 "where all ranges overlap".
的哪一部分
谢谢。
这有点难看,因为它同时使用了递归 CTE 以及 daterange 和 Postgres Range 函数,但我认为它能让您大致了解:
CREATE TEMP TABLE dr (id CHAR(1), range daterange);
INSERT INTO dr VALUES
('A', '2018-01-01', '2018-01-15'),
('B', '2018-01-01', '2018-01-02'),
('C', '2018-01-07', '2018-01-20'),
('D', '2018-01-12', '2018-01-30');
WITH RECURSIVE recRange AS
(
SELECT id,
range,
CAST(id as varchar(100)) as path,
1 as depth
FROM drrange
UNION ALL
SELECT drrange.id,
drrange.range * recRange.range,
CAST(recrange.path || ',' || drrange.id AS VARCHAR(100)),
recRange.depth + 1
FROM recRange INNER JOIN drrange ON
recRange.range && drrange.range
AND recRange.id < drrange.id
--Prevent cycling (which shouldn't happen with that join)
WHERE depth < 20
),
drrange AS
(
SELECT
id,
daterange(from_date, to_date + 1) as range
FROM dr
)
SELECT path as entities, range as period FROM recRange t1
WHERE depth > 1
-- Keep the range only if it is NOT contained by
--+ any other range that has a deeper depth then it
--+ and has the same ending id (id). This isn't the
--+ best logic and could make false positives, but...
--+ it's in the ballpark
AND NOT range <@ ANY(SELECT range FROM recRange WHERE depth > 1 AND depth > t1.depth AND id = t1.id);
entities | period
----------+-------------------------
A,B | [2018-01-01,2018-01-03)
A,C | [2018-01-07,2018-01-16)
C,D | [2018-01-12,2018-01-21)
A,C,D | [2018-01-12,2018-01-16)
我正在尝试解决与此类似的问题Find all intersections of all sets of ranges in PostgreSQL
不同之处在于,在这个线程中,它获取 所有 范围重叠的范围,而在我的用例中,我所有可能的重叠:
考虑4个范围如
[2018-01-01, 2018-01-15]
[2018-01-01, 2018-01-02]
[2018-01-07, 2018-01-20]
[2018-01-12, 2018-01-30]
创建这样的时间轴
A [==============]
B [=]
C [=============]
D [===============]
我想获取所有发生的重叠,所以:
{ entities: [A, B], period: [2018-01-01, 2018-01-02] }
{ entities: [A, C], period: [2018-01-07, 2018-01-11] }
{ entities: [A, C, D], period: [2018-01-12, 2018-01-15] }
{ entities: [C, D], period: [2018-01-15, 2018-01-20] }
另一件事,在结果中我需要同一组中最可能的重叠,这解释了为什么没有 A, D
重叠。
我已经在同一时期得到 A, C, D
并且没有只有 A, D
重叠的时期,而有一个 A, C
例如。
我设法使来自其他线程的查询与我的设置/表一起工作,但我不确定是否理解所有这些,尤其是 "where all ranges overlap".
的哪一部分谢谢。
这有点难看,因为它同时使用了递归 CTE 以及 daterange 和 Postgres Range 函数,但我认为它能让您大致了解:
CREATE TEMP TABLE dr (id CHAR(1), range daterange);
INSERT INTO dr VALUES
('A', '2018-01-01', '2018-01-15'),
('B', '2018-01-01', '2018-01-02'),
('C', '2018-01-07', '2018-01-20'),
('D', '2018-01-12', '2018-01-30');
WITH RECURSIVE recRange AS
(
SELECT id,
range,
CAST(id as varchar(100)) as path,
1 as depth
FROM drrange
UNION ALL
SELECT drrange.id,
drrange.range * recRange.range,
CAST(recrange.path || ',' || drrange.id AS VARCHAR(100)),
recRange.depth + 1
FROM recRange INNER JOIN drrange ON
recRange.range && drrange.range
AND recRange.id < drrange.id
--Prevent cycling (which shouldn't happen with that join)
WHERE depth < 20
),
drrange AS
(
SELECT
id,
daterange(from_date, to_date + 1) as range
FROM dr
)
SELECT path as entities, range as period FROM recRange t1
WHERE depth > 1
-- Keep the range only if it is NOT contained by
--+ any other range that has a deeper depth then it
--+ and has the same ending id (id). This isn't the
--+ best logic and could make false positives, but...
--+ it's in the ballpark
AND NOT range <@ ANY(SELECT range FROM recRange WHERE depth > 1 AND depth > t1.depth AND id = t1.id);
entities | period
----------+-------------------------
A,B | [2018-01-01,2018-01-03)
A,C | [2018-01-07,2018-01-16)
C,D | [2018-01-12,2018-01-21)
A,C,D | [2018-01-12,2018-01-16)