Проблема
У меня есть таблица записей, каждая из которых содержит id
, in_datetime
и out_datetime
. Запись считается «открытой» в течение времени между in_datetime
и out_datetime
. Я хочу знать, сколько записей времени было «открыто» для каждой минуты дня (независимо от даты). Например, за последние 90 дней я хочу знать, сколько записей было «открыто» в 3:14, затем в 3:15, затем в 3:16, затем ... Если в 2 не было «открытых» записей : 00 утра запрос должен возвращать 0 или ноль вместо исключения строки, поэтому всегда должно быть возвращено 1440 строк (количество минут в дне). Дата и время хранятся в UT C и должны быть приведены к часовому поясу.
Упрощенный пример графика c
record_id | time_range
| 0123456789 (these are minutes past midnight)
1 | =========
2 | ===
3 | =======
4 | ===
5 | ==
______________________
result 3323343210
Желаемый вывод
time | count of open records at this time
00:00 120
00:01 135
00:02 132
...
23:57 57
23:58 62
23:59 60
Будет возвращено не более 1440 записей, поскольку в течение дня будет только 1440 минут.
Что я пробовал
1.) В подзапросе я в настоящее время генерирует мелкие серии времен для всего диапазона каждой временной записи. Затем я группирую их по времени и получаю количество записей в минуту. Вот db-скрипка, использующая мой текущий запрос:
select
trs.minutes,
count(trs.minutes)
from (
select
generate_series(
DATE_TRUNC('minute', (time_records.in_datetime::timestamptz AT TIME ZONE 'America/Denver')),
DATE_TRUNC('minute', (time_records.out_datetime::timestamptz AT TIME ZONE 'America/Denver')),
interval '1 min'
)::time as minutes
from
time_records
) trs
group by
trs.minutes
Это работает, но довольно неэффективно и требует несколько секунд для запуска из-за размера моего Таблица. Кроме того, он исключает случаи, когда записи не были открыты. Я думаю, что каким-то образом я мог бы использовать оконные функции для подсчета количества перекрывающихся записей времени для каждой минуты дня, но я не совсем понимаю, как это сделать.
2.) Изменяя запрос Гордона Линоффа в его ответе ниже, я пришел к этому ( db-fiddle link ):
with tr as (
select
date_trunc('minute', (tr.in_datetime::timestamptz AT TIME ZONE 'America/Denver'))::time as m,
1 as inc
from
time_records tr
union all
select
(date_trunc('minute', (tr.out_datetime::timestamptz AT TIME ZONE 'America/Denver')) + interval '1 minute')::time as m,
-1 as inc
from
time_records tr
union all
select
minutes::time,
0
from
generate_series(timestamp '2000-01-01 00:00', timestamp '2000-01-01 23:59', interval '1 min') as minutes
)
select
m,
sum(inc) as changes_at_inc,
sum(sum(inc)) over (order by m) as running_count
from
tr
where
m is not null
group by
m
order by
m;
Это выполняется достаточно быстро, но ближе к концу дня (около 22 : 00 и далее в связанном примере) значения по какой-то причине становятся отрицательными. Кроме того, этот запрос некорректно работает с записями с интервалами времени, превышающими полночь. Это шаг в правильном направлении, но я, к сожалению, не понимаю этого достаточно, чтобы улучшить его.