【问题标题】:PostgreSQL query to detect overlapping time ranges用于检测重叠时间范围的 PostgreSQL 查询
【发布时间】:2022-03-25 22:39:00
【问题描述】:

我在 PostgreSQL 9.2 中有一个表,看起来像这样(简化):

CREATE TABLE my_features
(
  id integer NOT NULL,
  feature_id integer NOT NULL,
  begin_time timestamp NOT NULL,
  end_time timestamp
)

对于每个 feature_id,可能有多个行,其时间范围由 begin_time/end_time 指定。它们可能重叠,但这种情况比较少见。我正在寻找一种快速的方法来查找所有具有/不具有任何重叠的 feature_id。

我尝试使用窗口函数来做到这一点,如下所示:

SELECT feature_id, bool_or(end_time > lead(begin_time) OVER ts_win) OVER ts_win AS overlaps_any
FROM my_features
WINDOW ts_win AS (PARTITION BY feature_id ORDER BY begin_time)

...但这不起作用:

ERROR:  window function calls cannot be nested

算法很简单:按 begin_time 对给定 feature_id 的行进行排序,并检查是否有 end_time > 下一个 begin_time(如果有)。我怀疑一定有一种简单的方法可以做到这一点,也许使用 tsrange 函数,但现在似乎找不到。

【问题讨论】:

    标签: sql postgresql


    【解决方案1】:

    这确实可以使用范围类型来完成。

    以下选择所有具有重叠范围的行:

    select f1.*
    from my_features f1
    where exists (select 1
                  from my_features f2
                  where tsrange(f2.begin_time, f2.end_time, '[]') && tsrange(f1.begin_time, f1.end_time, '[]')
                    and f2.feature_id = f1.feature_id
                    and f2.id <> f1.id);
    

    当您将条件更改为 NOT EXISTS 时,您会发现没有任何重叠范围的那些。

    SQLFiddle 示例:http://sqlfiddle.com/#!15/40b1e/1

    tsrange(f2.begin_time, f2.end_time, '[]') 创建一个包含上限和下限的范围。您还可以创建排除一个或两个的范围。

    更多详情可以在手册中找到:
    http://www.postgresql.org/docs/current/static/rangetypes.html#RANGETYPES-INCLUSIVITY

    &amp;&amp; 运算符检查两个范围是否重叠:http://www.postgresql.org/docs/current/static/functions-range.html

    (我只是希望甲骨文有这样的奇思妙想......)

    【讨论】:

    • 谢谢,这行得通!现在我看到它看起来很简单。 :)
    • 太棒了!非常感谢它为我节省了很多时间,我只是在发现 postgre 和那种很酷的东西!
    • 非常感谢,今天救了我。
    【解决方案2】:

    这是一个观察。如果某项功能存在重叠时间段,则至少有一个时间段与begin_time 定义的前一个时间段重叠。 (你可以换个角度看。如果没有这样的重叠,那么在一个时间框架和下一个时间框架之间总是有一个差距,并且没有任何重叠。)

    这将导致以下用于确定重叠的查询:

    select f.feature_id
    from (select f.feature_id,
                 (case when lag(end_time) over (partition by feature_id order by begin_time) > begin_time
                       then 1 else 0
                  end) as HasOverlap
          from my_features f
         ) f
    group by f.feature_id
    having max(HaxOverlap) = 1;
    

    【讨论】:

    • 谢谢,这很有效,这基本上是我在原始帖子中尝试做的。我删除了“case when”,只是在比较结果上使用了 bool_or()。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-06-30
    • 2020-04-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多