【问题标题】:Get all possible intersections of multiple time ranges in PostgreSQL获取PostgreSQL中多个时间范围的所有可能交集
【发布时间】:2018-02-06 15:33:51
【问题描述】:

我正在尝试解决与Find all intersections of all sets of ranges in PostgreSQL类似的问题

不同之处在于,在这个线程中,它获取 all 范围重叠的范围,而在我的用例中,我所有可能的重叠:

考虑 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则有一个。

我设法使来自另一个线程的查询与我的设置/表一起工作,但我不确定是否理解所有这些,尤其是“所有范围重叠的地方”的哪一部分。

谢谢。

【问题讨论】:

  • 为什么A, D 被排除在您的预期输出之外?我不明白为什么A, DA, C 不同。
  • @MikeSherrill'CatRecall' 因为没有重叠部分,只有A, D 重叠,所以总是同时有C,我总是需要它们按大多数实体分组当下。我会尽量让问题更清楚,我没有想到这个用例。

标签: sql postgresql date-range


【解决方案1】:

这有点难看,因为它同时使用了递归 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)

【讨论】:

  • 感谢您的回复,它似乎非常适合我的问题!我很好奇怎么会发生误报?
  • 你可能是对的,他们不会。不过,我会谨慎行事,并在有和没有最后一点 WHERE 的情况下运行它,以确定您的实时数据会丢失什么。如此多的重叠时期在这里被标准化,很难考虑所有潜在的情况。
  • 最后一点肯定有帮助,我会尝试一下,但我想我可以将其标记为解决方案,再次感谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-04-14
  • 1970-01-01
  • 2018-03-04
  • 1970-01-01
  • 2013-09-12
相关资源
最近更新 更多