重叠时间帧的SQL解决方案

时间:2016-01-28 08:46:19

标签: sql postgresql

我有一组机器。他们偶尔跑步和停止。这些运行时间会自动记录到postgres表中,时间戳为starttimeendtime

我需要在6小时内找到每台机器的运行时间。这就是我到目前为止所做的:

SELECT machine, SUM(EXTRACT(EPOCH FROM (endtime - starttime)))
FROM table
WHERE 
      starttime >= '2016-01-27 12:00:00'
      AND starttime <= '2016-01-27 18:00:00'
GROUP BY machine
ORDER BY machine

这样可行,我可以在一段时间内通过机器获得运行时间。但它有一个缺陷 - 任何在12:00之前开始的运行时间都不会被计算在内。任何在我的时间框架内开始的运行时间,但是在它有时间计数之后才会结束,不应该在那里。

是否有解决方案只能提取时间范围内的时间?我最初的想法是选择所有行:

endtime >= '2016-01-27 12:00:00'

以某种方式,在内存中,将所有开始时间设置为'2016-01-27 12:00:00' 其中开始时间早于和:

starttime <='2016-01-27 18:00:00'

并且,在没有更新数据库的情况下再次在内存中,将所有结束时间设置为'2016-01-27 18:00:00',其中结束时间晚于此时间。然后运行提取/求和查询。

但是我在努力实现这样的事情。我有一个使用Java / Python的工作解决方案,这些数据被返回,但它们是迭代的并且花费的时间比我想要的多。如果可能的话,我真的很想找到一个SQL解决方案。

编辑:为了澄清,我需要计算在时间范围内发生的所有运行时间 - 如果运行在时间范围之前开始,那么只有在该时间范围之后发生的那部分运行应该被计算在内。

3 个答案:

答案 0 :(得分:1)

您可以使用重叠运算符:

SELECT machine, SUM(EXTRACT(EPOCH FROM (endtime - starttime)))
FROM table
where (starttime, endtime) overlaps (timestamp '2016-01-27 12:00:00', timestamp '2016-01-27 18:00:00')
GROUP BY machine
ORDER BY machine

答案 1 :(得分:1)

编辑:这就是你需要的。

当它在范围之间开始并在之后结束时,它将在18:00:00结束它。

同样适用于在范围和之前开始之间结束时,它将在12:00:00

开始计算
SELECT machine, SUM(EXTRACT(EPOCH FROM (endtime - starttime)))
FROM (SELECT machine,
             case when starttime <= '2016-01-27 12:00:00' then '2016-01-27 12:00:00' else starttime end as starttime,
             case when endtime>= '2016-01-27 18:00:00' then '2016-01-27 18:00:00' else endtime end as endtime,  FROM table
    WHERE 
          (endtime>= '2016-01-27 12:00:00'
          AND endtime <= '2016-01-27 18:00:00')
          OR   (starttime>= '2016-01-27 12:00:00'
          AND starttime<= '2016-01-27 18:00:00')
GROUP BY machine
ORDER BY machine

答案 2 :(得分:1)

使用类型tsrange及其intersection运算符。

示例数据:

create table machines (machine int, starttime timestamp, endtime timestamp);
insert into machines values
(1, '2016-01-27 10:00:00', '2016-01-27 14:00:00'),
(2, '2016-01-27 15:00:00', '2016-01-27 16:00:00'),
(3, '2016-01-27 17:00:00', '2016-01-27 20:00:00');

此查询将时间范围舍入返回给定时间段:

select 
    machine, 
    tsrange(starttime, endtime)* 
        '[2016-01-27 12:00:00, 2016-01-27 18:00:00)'::tsrange t
from machines;

 machine |                       t                       
---------+-----------------------------------------------
       1 | ["2016-01-27 12:00:00","2016-01-27 14:00:00")
       2 | ["2016-01-27 15:00:00","2016-01-27 16:00:00")
       3 | ["2016-01-27 17:00:00","2016-01-27 18:00:00")
(3 rows)    

从以上数据集中获取聚合:

select 
    machine, 
    sum(extract(epoch from (upper(t) - lower(t))))
from (
    select 
        machine, 
        tsrange(starttime, endtime)* 
            '[2016-01-27 12:00:00, 2016-01-27 18:00:00)'::tsrange t
    from machines
    ) sub
group by 1
order by 1;

 machine | sum  
---------+------
       1 | 7200
       2 | 3600
       3 | 3600
(3 rows)