在Postgres SQL中删除多余的时间戳范围

时间:2019-06-17 09:16:10

标签: sql postgresql timestamp sql-timestamp

我有一个表,该表包含文件名和每个文件的时间戳范围,例如:

       filename           mindatetime                 maxdatetime
    monitor_4.dat   2019-04-28 09:00:00 AM      2019-04-29 11:00:00 AM
    monitor_4.dat   2019-04-28 11:00:00 AM      2019-04-29 18:00:00 PM
    monitor_4.dat   2019-04-28 09:30:00 AM      2019-04-29 23:00:00 PM
    monitor_5.dat   2019-04-28 07:00:00 AM      2019-04-28 10:00:00 AM
    monitor_5.dat   2019-04-28 02:00:00 PM      2019-04-28 06:00:00 PM
    monitor_5.dat   2019-04-28 09:00:00 AM      2019-04-28 03:00:00 PM
    monitor_7.dat   2019-04-21 03:06:26.0 AM    2019-05-21 03:06:10.0 AM
    monitor_7.dat   2019-05-21 03:06:10.001 AM  2019-05-24 03:06:11.0 AM
    monitor_7.dat   2019-06-05 03:06:18.001 AM  2019-06-06 03:06:11.0 AM
    monitor_7.dat   2019-05-24 03:06:11.001 AM  2019-06-05 03:06:18.0 AM
    monitor_7.dat   2019-05-12 07:00:10.001 AM  2019-05-13 10:00:10.000 AM
    monitor_7.dat   2019-05-15 09:30:10.001 AM  2019-05-18 11:30:10.000 AM

我需要删除多余的时间戳范围,即那些属于给定时间戳范围的时间戳。 在这种情况下,对于文件“ monitor_5.dat”,我们需要将mindatetime设置为7AM,将maxdatetime设置为6PM,因为它们形成了涵盖其他条目的逻辑最小和最大时间戳范围。

所以我的输出结果应该像这样:

   filename           mindatetime                 maxdatetime
monitor_4.dat   2019-04-28 09:00:00 AM      2019-04-29 23:00:00 PM
monitor_5.dat   2019-04-28 07:00:00 AM      2019-04-28 06:00:00 PM
monitor_7.dat   2019-04-21 03:06:26.0 AM    2019-06-05 03:06:18.0 AM
monitor_7.dat   2019-05-12 07:00:10.001 AM  2019-05-13 10:00:10.000 AM
monitor_7.dat   2019-05-15 09:30:10.001 AM  2019-05-18 11:30:10.000 AM

这仅需要通过SQL来实现。任何的意见都将会有帮助。我已经研究过Postgres中提供的“ tsrange”功能,但这并不能完全帮助我。

2 个答案:

答案 0 :(得分:1)

此答案仅适用于PostgreSQL。您是否故意标记了MySQL?

您必须将表与其自身连接起来,并删除其他条目中包含的条目:

DELETE FROM mytable AS a
USING mytable AS b
WHERE a.filename = b.filename
  AND tsrange(a.mindatetime, a.maxdatetime) <@ tsrange(b.mindatetime, b.maxdatetime)
  AND (a.ctid, a.xmin::text) <> (b.ctid, b.xmin::text);

最后一个条件阻止将行与自身进行比较。

答案 1 :(得分:1)

这是一种空白和岛屿问题。我建议找到“重叠”发生的位置,然后使用累积总和来识别组。累积max()为此:

select filename, min(mindatetime) as mindatetime,
       max(maxdatetime) as maxdatetime
from (select t.*,
             sum(case when prev_maxdatetime >= mindatetime then 0 else 1 end) over
                 (partition by filename order by mindatetime) as grp
      from (select t.*,
                   max(maxdatetime) over
                       (partition by filename
                        order by mindatetime
                        rows between unbounded preceding and 1 preceding
                       ) as prev_maxdatetime
            from t
           ) t
     ) t
group by filename, grp;

最里面的子查询确定不重叠时间范围的起始位置。然后,中间查询对这些“开始”进行累加总和,以将分组标识符分配给每个组。然后,外部查询将按该组(和文件名)进行聚合。

您可以运行内部查询并查看它们产生什么值。

Here是db <>小提琴。就我而言,根据您在此处提出的问题,此方法可以正常工作。如果您有一个时间滞后要考虑这些值相同/重叠,请提出一个 new 问题,并清楚地说明您的逻辑。

编辑:

如果每个文件名只需要一条记录,则只需使用聚合:

select filename, min(mindatetime) as mindatetime,
       max(maxdatetime) as maxdatetime
from t
group by filename;

第一个版本合并了行之间没有空格的行。这只是最早和最新的日期/时间。