【问题标题】:Cumulative elapsed minutes on hourly basis in PostgreSQL在 PostgreSQL 中每小时累积经过的分钟数
【发布时间】:2020-07-13 03:08:19
【问题描述】:

我有一个日期时间列。我需要导出从每小时的第一个值到最后一个值的总分钟数列,按小时分组,但是,在重叠事件的情况下,时间应该分布在两个小时之间。还有一种情况,如果两个连续记录之间经过的时间超过30分钟,则必须忽略。

下面,我分三个阶段进行了解释,原始、中间(计算运行总数)和最终。

而且,我计划在同一时间获取每小时增量数据,因此,我们如何正确地将其与旧数据合并是另一个问题。

样本数据:

 Moves_TS
1/4/2020 10:00
1/4/2020 10:25
1/4/2020 10:42
1/4/2020 10:56
1/4/2020 10:59
1/4/2020 11:02
1/4/2020 11:24
1/4/2020 11:43
1/4/2020 11:55
1/4/2020 12:26
1/4/2020 12:29

中间层:

Moves_TS    Hour    Running Total
1/4/2020 10:00  10  0
1/4/2020 10:25  10  25
1/4/2020 10:42  10  42
1/4/2020 10:56  10  56
1/4/2020 10:59  10  60
1/4/2020 11:02  11  2
1/4/2020 11:24  11  24
1/4/2020 11:43  11  43
1/4/2020 11:55  11  55
1/4/2020 12:26  12  0
1/4/2020 12:29  12  3

最终输出:

Hour    Work done/Hour
10  60
11  55
12  3

【问题讨论】:

    标签: sql postgresql window-functions cumulative-sum


    【解决方案1】:

    这是一个带有一些曲折的缝隙和孤岛问题。首先,我将通过 30 分钟的间隔定义的“岛屿”进行总结:

    select min(moves_ts) as start_ts, max(moves_ts) as end_ts
    from (select o.*,
                 count(prev_moves_ts) filter (where moves_ts > prev_moves_ts + interval '30 minute') over (order by moves_ts) as grp
          from (select o.*, lag(moves_ts) over (order by moves_ts) as prev_moves_ts
                from original o
               ) o
         ) o
    group by grp;
    

    然后您可以将其与generate_series() 一起使用来扩展数据并计算每小时的重叠:

    with islands as (
          select min(moves_ts) as start_ts, max(moves_ts) as end_ts
          from (select o.*,
                       count(prev_moves_ts) filter (where moves_ts > prev_moves_ts + interval '30 minute') over (order by moves_ts) as grp
                from (select o.*, lag(moves_ts) over (order by moves_ts) as prev_moves_ts
                      from original o
                     ) o
               ) o
          group by grp
         )
    select hh.hh,
           sum( least(hh.hh + interval '1 hour', i.end_ts) -
                greatest(hh.hh, i.start_ts)
              ) as duration           
    from (select generate_series(date_trunc('hour', min(moves_ts)),
                                 date_trunc('hour', max(moves_ts)),
                                 interval '1 hour'
                                ) hh
          from original o
         ) hh left join
         islands i
         on i.start_ts < hh.hh + interval '1 hour' and
            i.end_ts >= hh.hh
    group by hh.hh
    order by hh.hh;
    

    Here 是一个 dbfiddle。

    【讨论】:

    • 感谢您的快速回复。这个例子很简单,只有 TS 字段,但实际上源表很宽,有很多字段用于聚合,现在你已经添加了 generate_series 和其他复杂的函数,我无法在我的原始数据集中复制即使我已经成功模拟了您的第一个查询解决方案。即使我无法共享数据,但您是否希望我更新问题,至少包含输入字段名称和预期的输出字段名称以便更好地理解?
    • @AakashBasu 。 . .我修复了代码并添加了一个 dbfiddle.
    • 您能否阅读上述评论并检查编辑部分。
    • @AakashBasu 。 . .我想你应该问另一个问题。目前尚不清楚您希望如何处理其他字段。样本数据和期望的结果会有所帮助。
    【解决方案2】:
    select 
       MOVES_TS, 
       Hour, 
       TO_CHAR(MOVES_TS,'YYYYMMDDHH') DATEHR,
       MIN(Moves_TS) over (partition by DATEHR) as MIN_MOVES_TS,
       (
        DATE_PART('day', MOVES_TS - MIN_MOVES_TS) * 24 +
       DATE_PART('hour', MOVES_TS - MIN_MOVES_TS) * 60 + 
       DATE_PART('minute', MOVES_TS - MIN_MOVES_TS)
       ) as RunningTotal
    
    from dataset
    
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-09-09
      • 1970-01-01
      • 1970-01-01
      • 2017-07-29
      • 1970-01-01
      • 2014-05-15
      相关资源
      最近更新 更多