如何计算postgresql中多个记录的开始和结束时间戳之间的总时间

时间:2014-01-21 01:41:16

标签: sql postgresql timestamp

我有一个postgresql 9.1表,其中包含开始和结束时间戳,用于记录可能与userid重叠的时间段。例如:

Userid   Begin                     End
1        2014-01-19 21:14:59+00    2014-01-19 21:30:00+00
2        2014-01-19 21:19:29+00    2014-01-19 21:40:30+00
1        2014-01-19 21:16:29+00    2014-01-19 21:31:30+00
3        2014-01-19 21:15:22+00    2014-01-19 21:30:29+00
2        2014-01-19 21:29:59+00    2014-01-19 21:45:00+00
3        2014-01-19 21:15:25+00    2014-01-19 21:35:10+00
4        2014-01-19 22:00:01+00    2014-01-19 22:05:20+00

我需要获得这些行中每个Userid的所有分钟数的总和,确保单个Userid的时间不会重复计算,因此输出将为:

Userid   Hour      Total Minutes
1        21        n
2        21        n
3        21        n
4        22        n

1 个答案:

答案 0 :(得分:1)

这是一个难题,但至少Postgres具有lag()功能。

这是个主意。假设你有重叠。找到没有重叠的第一个元素。给它一个标志值1.然后做这个值的累积和。结果是,现在根据不同的“岛屿”为不同的时间段分配值。然后简单的聚合工作。

以下内容适用于许多情况,使用lag()

select userid, sum(secs) / 60 as minutes
from (select userid, Island, min(begin) as begin, max(end) as end,
             extract(epoch from max(end) - min(begin)) as secs
      from (select t.*,
                   sum(IslandBegin) over (partition by userid order by begin) as Island
            from (select t.*,
                         (case when lag(end) over (partition by userid order by begin) >= begin
                               then 0
                               else 1
                          end) as IslandBegin
                  from table t
                 ) t
           ) t
      group by userid, Island
     ) t
group by userid;

请注意,end是保留字,因此请相应地调整代码。

以上并不总是奏效。它假设重叠与前一个开始,并且可能不是这种情况。考虑{(1,100),(2,5),(8,10)}。我认为正确的逻辑仍然需要相关的子查询。最里面的查询需要从:

更改
            from (select t.*,
                         (case when lag(end) over (partition by userid order by begin) >= begin
                               then 0
                               else 1
                          end) as IslandBegin
                  from table t
                 ) t

为:

            from (select t.*,
                         coalesce((select 1
                                   from table t2
                                   where t2.end >= t.begin and
                                         t2.begin < t.begin
                                   limit 1
                                  ), 0
                                 ) as IslandBegin
                  from table t
                 ) t

实际上,这并不是那么糟糕,我了解到lag()不能用于所有这些情况。