Kesulitan khusus adalah untuk tidak melewatkan rentang waktu ke kerangka waktu luar.
Dengan asumsi bahwa baris berikutnya untuk id
yang diberikan selalu memiliki status yang berlawanan.
Menggunakan nama kolom ts
bukannya 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;
Mengembalikan interval seperti yang diminta.
ID tanpa entri dalam rentang waktu yang dipilih tidak akan muncul. Anda harus menanyakannya sebagai tambahan.
SQL Fiddle.
Catatan:Saya melemparkan total_disconnected
yang dihasilkan ke text
di biola, karena jenis interval
ditampilkan dalam format yang mengerikan.
Tambahkan ID tanpa entri dalam jangka waktu yang dipilih
Tambahkan ke kueri di atas (sebelum ORDER BY 1
terakhir ):
...
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;
Sekarang, hanya ID tanpa entri di atau sebelumnya rentang waktu yang dipilih tidak muncul.