我有一个表,记录了实体的每一次状态变化
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_disconnected
至 text
在 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;
现在,只有在或之前所选时间范围内没有条目的 ID 不会显示。
关于sql - 行间时间差之和,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22114645/