Sql 行之间的时间差之和

Sql 行之间的时间差之和,sql,postgresql,aggregate-functions,window-functions,Sql,Postgresql,Aggregate Functions,Window Functions,我有一个记录实体状态变化的表格 id recordTime Status ID1 2014-03-01 11:33:00 Disconnected ID1 2014-03-01 12:13:00 Connected ID2 2014-03-01 12:21:00 Connected ID1 2014-03-01 12:24:00 Disconnected ID1 2014-03-01 12:29:00 Connected ID2 2014-03-01

我有一个记录实体状态变化的表格

id      recordTime        Status
ID1 2014-03-01 11:33:00 Disconnected  
ID1 2014-03-01 12:13:00 Connected  
ID2 2014-03-01 12:21:00 Connected  
ID1 2014-03-01 12:24:00 Disconnected  
ID1 2014-03-01 12:29:00 Connected  
ID2 2014-03-01 12:40:00 Disconnected  
ID2 2014-03-01 13:03:00 Connected  
ID2 2014-03-01 13:13:00 Disconnected  
ID2 2014-03-01 13:29:00 Connected  
ID1 2014-03-01 13:30:00 Disconnected
我需要计算总非活动时间,即给定时间窗口内每个ID的“已连接”和上次“已断开”状态之间的时间

对于上表和2014-03-01 11:00:00至2014-03-01 14:00:00的时间范围,输出应为:

ID  InactiveTime
ID1  01:15:00
ID2  02:00:00
但它输出:

"ID1";"00:45:00"
"ID2";"00:39:00"

我就是这样理解你的问题的


你能解释一下你想要的输出吗?

特别的困难是不要错过到外部时间框架的时间跨度。 假设任何给定id的下一行始终具有相反的状态。 使用列名ts而不是recordTime:

按要求返回间隔。 所选时间范围内没有条目的ID不会显示。您必须另外查询它们

注意:我将结果total_转换为小提琴中的文本,因为类型间隔显示的格式很糟糕

在选定的时间范围内添加不带条目的ID 每一个请求在评论中。 在最终订单之前添加到上面的查询1:


现在,只有没有条目的ID在选定的时间范围内或之前没有出现。

您是否确信您的期望输出?是否保证下一行的任何给定ID具有相反的状态?@ Houial:输出是正确的,如果您考虑给定的时间范围2014-03-01 11:00到2014-03-01 14:00 00。@欧文,是的,每个条目的状态都会进行明确的切换。@ErwinBrandstetter-oups我看到:@Houari:也遗漏了相同的基本细节;正如欧文在他的回答中提到的,特别困难的是不要错过外部时间框架的时间跨度。如果我将时间范围从11:00更改为14:00,从13:00更改为14:00,为什么会显示混乱的结果?@Sandeep:我的第一个版本没有为时间范围之外的行做好准备。考虑更新版本的更新小提琴。这真的很快。谢谢!对于所选时间范围内没有条目的注释ID,不会显示。你必须另外询问他们,你的建议是什么?我的答案基本上是你的一个增强版,它也考虑了在所选列表中没有条目的ID,你觉得我的答案怎么样time@Sandeep请考虑这个附录。
"ID1";"00:45:00"
"ID2";"00:39:00"
select id, sum(diff) as inactive
from (
    select
        recordtime,
        recordTime -
            lag(recordTime, 1, recordTime)
            over(
                partition by id
                order by recordTime
            )
        as diff,
        status,
        id
    from t
) s
where status = 'Connected'
group by id
order by id
;
 id | inactive 
----+----------
  1 | 00:45:00
  2 | 00:39:00
WITH span AS (
   SELECT '2014-03-01 13:00'::timestamp AS s_from  -- start of time range
        , '2014-03-01 14:00'::timestamp AS s_to    -- end of time range
   )
, cte AS (
   SELECT id, ts, status, s_to
        , lead(ts, 1, s_from) OVER w AS span_start
        , first_value(ts)     OVER w AS last_ts
   FROM   span s
   JOIN   tbl  t ON t.ts BETWEEN s.s_from AND s.s_to
   WINDOW w AS (PARTITION BY id ORDER BY ts DESC)
   )
SELECT id, sum(time_disconnected)::text AS total_disconnected
FROM  (
   SELECT id, ts - span_start AS time_disconnected
   FROM   cte
   WHERE  status = 'Connected'

   UNION  ALL  
   SELECT id, s_to - ts
   FROM   cte
   WHERE  status = 'Disconnected'
   AND    ts = last_ts
   ) sub
GROUP  BY 1
ORDER  BY 1;
...
UNION  ALL
SELECT id, total_disconnected
   FROM  (
   SELECT DISTINCT ON (id)
          t.id, t.status, (s.s_to - s.s_from)::text AS total_disconnected
   FROM   span     s
   JOIN   tbl      t ON t.ts < s.s_from  -- only from before time range
   LEFT   JOIN cte c USING (id)
   WHERE  c.id IS NULL         -- not represented in selected time frame
   ORDER  BY t.id, t.ts DESC   -- only the latest entry
   ) sub
WHERE  status = 'Disconnected' -- only if disconnected
ORDER  BY 1;