【发布时间】:2020-01-15 19:18:23
【问题描述】:
问题
我有一个记录表,每个记录包含id、in_datetime 和out_datetime。在in_datetime 和out_datetime 之间的时间段内,记录被视为“打开”。我想知道一天中每一分钟有多少时间记录“打开”(无论日期如何)。例如,对于过去 90 天,我想知道在凌晨 3 点 14 分、凌晨 3 点 15 分、凌晨 3 点 16 分、然后……如果没有记录在凌晨 2 点“打开” :00 am 查询应返回 0 或 null 而不是排除该行,因此应始终返回 1440 行(一天中的分钟数)。日期时间以 UTC 格式存储,需要转换为时区。
简化示例图形
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.) 在子查询中,我目前为每个时间记录的整个范围生成了一系列时间。然后我按时间对它们进行分组并计算每分钟的记录数。 Here is a db-fiddle using my current query:
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.) 在下面的回答中修改 Gordon Linoff 的查询,我来到了这个 (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 开始),由于某种原因,值变为负数。此外,此查询似乎不适用于时间范围跨越午夜的记录。这是朝着正确方向迈出的一步,但不幸的是,我对它的理解还不够深入,无法进一步改进。
【问题讨论】:
标签: sql postgresql window-functions postgresql-10