【问题标题】:How to split time intervals by day in PostgreSQL如何在 PostgreSQL 中按天分割时间间隔
【发布时间】:2019-09-18 17:59:26
【问题描述】:

我有一个查询,它返回一些带有设备 ID 和时间戳的设备状态信息。我正在尝试做一些每日报告,为此我需要知道白天有哪些状态设备。因此,例如,我的查询可能会得到这样的结果

device id     start              end                state
---------------------------------------------------------
1             2017-01-01 13:38   2017-01-03 12:47   1
2             2017-01-01 03:15   2017-01-02 11:04   1
... more records for devices including devices 1 and 2 ...

我想要的结果是

device id     start              end                state
---------------------------------------------------------
1             2017-01-01 13:38   2017-01-01 23:59   1
1             2017-01-02 00:00   2017-01-02 23:59   1
1             2017-01-03 00:00   2017-01-03 12:47   1
2             2017-01-01 03:15   2017-01-01 23:59   1
2             2017-01-02 00:00   2017-01-02 11:04   1

我试过的,是这样的

select
    l.device_id,
    gs.ts as "day",
    case when l.start < gs.ts then gs.ts else l.start end as start,
    case when l.end > gs.ts + '1 day'::interval then gs.ts + '1 day'::interval else l.end end as end,
    l.state
from ( ... my query goes here ... ) as l
right join
    (select generate_series(
        date 'start date',
        date 'end date',
        '1 day'::interval)) as gs(ts)
    on ((gs.ts, gs.ts + '1 day'::interval) overlaps (l.start, l.end))
order by l.device_id, l.start

本质上,我使用重叠函数正确加入一系列天,因此与该天重叠的每个间隔都会生成一行,然后我在日期边界处切割间隔。

然后我将其用作我日常计算的嵌套选择。

这种方法的问题在于,正确的连接会生成大量记录,然后连接过滤器会永远占用。这是explain analyze输出的一段

->  Nested Loop Left Join  (cost=5371.28..3149290.69 rows=11525332 width=32) (actual time=228.799..32849.000 rows=41197 loops=1)
Join Filter: ... the generate sequence stuff removed for brevity...
Rows Removed by Join Filter: 4994476

如您所见,它生成了大约 500 万行,将它们过滤到 41K 行,操作耗时约 32 秒。

这个问题有更有效的解决方案吗?

【问题讨论】:

    标签: sql postgresql


    【解决方案1】:

    这应该比您当前的方法更快:

    select q.device_id,
           generate_series(start::date, end::date, interval '1 day') as day,
           end as day,
           state
    from (your query here) q;
    

    您可以使用子查询来获取所需的确切日期/时间。

    【讨论】:

    • 因此,这将为每条记录生成一个完整的序列。那么我该如何在休息时间拆分记录呢?
    • @MadWombat 。 . .如果你在一个数据样本上运行它,你会看到 Postgres 每天生成一个单独的行。
    • 我明白了。然后我使用 where 子句将其过滤掉,所以只保留开始和结束之间重叠的日子?
    • @MadWombat 。 . .您不需要额外的过滤。这应该生成您想要的行。你确实需要一些额外的逻辑来制定你想要显示的确切日期/时间的规则。
    • 哇!太棒了,从 30 多秒到大约 2 秒。
    猜你喜欢
    • 1970-01-01
    • 2019-06-28
    • 1970-01-01
    • 2015-09-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-07-18
    相关资源
    最近更新 更多