Особената трудност е да не се пропуснат времевите интервали до външната времева рамка.
Приемайки, че следващият ред за всеки даден 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;
Връща интервали според заявките.
Идентификаторите без записи в избрания времеви диапазон не се показват. Ще трябва да ги запитате допълнително.
SQL Fiddle.
Забележка:Прехвърлям получения total_disconnected
към text
в цигулката, защото типът interval
се показва в ужасен формат.
Добавяне на идентификатори без въвеждане в избраната времева рамка
Добавете към заявката по-горе (преди крайния 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;
Сега само идентификатори без записи в или преди избраният период от време не се показва.