Postgres:如何在时间段中检查多个值的记录

问题描述

我有一个具有以下形状的POSTGRES表:

sensor_id |         recorded_at
----------+--------------------
        A | 2020-01-01 00:00:00
        A | 2020-01-01 00:03:00
        B | 2020-01-01 01:00:00
        C | 2020-01-01 01:03:00
      ... |                 ...

给出一个

  • 开始时间
  • 结束时间
  • 桶宽
  • 传感器ID列表

我想编写一个查询,将范围[start_time,end_time]切成宽度bucket_width的子间隔(存储桶),并针对每个存储桶检查列表中的每个传感器是否具有在该存储桶中包含recorded_at的记录。

例如,假设输入

  • 开始时间= '2020-01-01 00:00:00'
  • 结束时间= '2020-01-01 02:00:00'
  • 桶宽= '1 hour'
  • 传感器ID列表= ['A','B','C']

查询应返回类似

内容
                  t1 |                  t2 | A_count | B_count | C_count
---------------------+---------------------+---------+---------+--------
 2020-01-01 00:00:00 | 2020-01-01 01:00:00 |       2 |       0 |       0
 2020-01-01 01:00:00 | 2020-01-01 02:00:00 |       0 |       1 |       1

我不需要实际计数,因此我想LIMIT 1会出现在某个地方。我只是在示例中包含了它,以帮助弄清楚我在寻找什么。

解决方法

一个选项使用generate_series()来生成行,然后使用left join来生成表,最后使用条件聚合来按传感器获取计数:

select s.ts ts1,s.ts + interval '1 hour' ts2,count(*) filter (where sensor_id = 'A') a_count,count(*) filter (where sensor_id = 'B') b_count,count(*) filter (where sensor_id = 'C') c_count
from generate_series('2020-01-01 00:00:00'::timestamp,'2020-01-01 02:00:00'::timestamp,'1 hour') s(ts)
left join mytable t on t.recorded_at >= s.ts and t.recorded_at < s.ts + interval '1 hour'
group by s.ts

请注意,这会产生一条额外的记录(来自02:00:00 to 03:00:00 ') as compared to your desired results. If you want to avoid that,you can slightly modify the generate_series()`参数,例如:

...
    from generate_series(
        '2020-01-01 00:00:00'::timestamp,'2020-01-01 02:00:00'::timestamp - interval '1 second','1 hour'
     ) s(ts)
...