Khó khăn đặc biệt là không bỏ lỡ các khoảng thời gian đến khung thời gian bên ngoài.
Giả sử rằng hàng tiếp theo cho bất kỳ id
nào đã cho luôn có trạng thái ngược lại.
Sử dụng tên cột ts
thay vì 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;
Trả về các khoảng thời gian được yêu cầu. Các ID
không có mục nhập trong phạm vi thời gian đã chọn sẽ không hiển thị. Bạn sẽ phải truy vấn thêm chúng.
SQL Fiddle.
Lưu ý:Tôi truyền total_disconnected
kết quả thành text
trong fiddle, bởi vì loại interval
được hiển thị ở định dạng khủng khiếp.
Thêm ID mà không cần nhập trong khung thời gian đã chọn
Thêm vào truy vấn ở trên (trước ORDER BY 1
cuối cùng ):
...
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;
Giờ đây, chỉ những ID không có mục nhập hoặc trước đó phạm vi thời gian đã chọn không hiển thị.