Postgres检查表行中的时间戳范围是否重叠

时间:2018-10-15 08:03:42

标签: sql postgresql datetime time-series

我们有一个Postgres表(实例化视图),其中包含约200万行,其中的列如下:

  • start_time(timestampz)-具有索引
  • end_time(时间戳)-具有索引

对于表中的每一行,我们想添加一个包含以下内容的结果列:

  • 1,如果行的开始和结束时间范围与任何其他行重叠
  • 0,如果行的开始和结束时间范围与其他任何行都不重叠

将行标记为重叠(1或0)的有效方法是什么?

编辑:

预期输出如下:

  • row_id
  • has_overlap-布尔值或整数(1或0)

1 个答案:

答案 0 :(得分:3)

我认为不会有一个真正快速的解决方案,因为它确实需要将表中的每一行与表中的每一行(或者至少是指定范围内的每一行)进行比较。

假设表的主键列名为id,则可以使用Postgres的range函数检查重叠行:

with check_period (check_range) as (
   values ( tstzrange(timestamptz '2018-10-01 00:00:00', timestamptz '2018-10-14 20:15:00') )
)
select id, 
       start_Time, 
       end_time, 
       exists (select *
        from the_table t2
           cross join check_perioud
        where t2.id <> t1.id 
        and tstzrange(t1.start_time, t1.end_time) && tstzrange(t2.start_time, t2.start_time)
        and tstzrange(t2.start_time, t2.start_time) <@ check_range
       ) has_overlapping_rows
from the_table t1
  cross join check_period
where tstzrange(t1.start_time, t1.end_time) <@ check_range;

CTE check_period仅在此处,因此不会重复要分析的时间段的值。如果您不想重复操作,可以将其删除:

select id, 
       start_Time, 
       end_time, 
       exists (select *
        from the_table t2
        where t2.id <> t1.id 
        and tstzrange(t1.start_time, t1.end_time) && tstzrange(t2.start_time, t2.start_time)
        and tstzrange(t2.start_time, t2.start_time) <@ tstzrange(timestamptz '2018-10-01 00:00:00', timestamptz '2018-10-14 20:15:00')
       ) has_overlapping_rows
from the_table t1
where tstzrange(t1.start_time, t1.end_time) <@ tstzrange(timestamptz '2018-10-01 00:00:00', timestamptz '2018-10-14 20:15:00');

您应该在时间戳范围上创建索引以使其快速:

create index on the_table( (tstzrange(start_time, end_time), id );

您可以扩展以上查询以返回重叠行的计数,而不是返回true / false标志:

select id, 
       start_Time, 
       end_time, 
       (select count(*)
        from the_table t2
        where t2.id <> t1.id 
        and tstzrange(t1.start_time, t1.end_time) && tstzrange(t2.start_time, t2.start_time)
        and tstzrange(t2.start_time, t2.start_time) <@ tstzrange(timestamptz '2018-10-01 00:00:00', timestamptz '2018-10-14 20:15:00')
       ) has_overlapping_rows
from the_table t1
where tstzrange(t1.start_time, t1.end_time) <@ tstzrange(timestamptz '2018-10-01 00:00:00', timestamptz '2018-10-14 20:15:00');

但是对于具有许多重叠行的行,这样做会比较慢,因为count(*)会强制数据库检查所有重叠行。 exists()解决方案可以在找到的第一行停止。