sql - 行间时间差之和

标签 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 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

最佳答案

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

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;

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

SQL Fiddle.
注意:我投了结果 total_disconnectedtext在 fiddle 中,因为类型 interval以糟糕的格式显示。

添加在选定时间范围内未进入的ID

根据评论中的要求。
添加到上面的查询(在最后一个 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;

SQL Fiddle.

现在,只有在或之前所选时间范围内没有条目的 ID 不会显示。

关于sql - 行间时间差之和,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22114645/

相关文章:

sql - 带有连接表的 PostgreSQL 分区 - 查询计划中未使用分区约束

mysql - Postgresql 与 MySQL : how do their data sizes compare to each other?

Ubuntu 上的 Perl Ora2Pg

sql - 计算连接表的列

mysql - 在 MySQL 中批量插入 SQL 查询

INNER JOIN 一次用于两个表的 SQL INSERT 语句

mysql - 在内部查询中使用 array_agg 和数组字符串

SQLite - 帮助优化具有多个条件的先前行的总计

sql - 使用上下文连接时不需要将 null 转换为 DBNull

mysql - 选择时间戳最接近的行