【问题标题】:SQL Timeseries - Group rows by gap in inactivity (session window)SQL Timeseries - 按非活动间隔对行进行分组(会话窗口)
【发布时间】:2021-12-14 10:24:50
【问题描述】:

我正在尝试将数据分组到由不活动分隔的会话中,或在 postgres 中时间戳列中的间隙。 (作为参考,这在 kSQL kafka 流世界中称为“session window”。)

例如,假设我有一系列来自 IoT 设备的测量值。每行都有一个时间戳和一个值,但设备并不总是发送数据,只有在需要时才会发送数据,因此存在不活动的间隙。

    timestamp         |  value

2021-10-29 11:05:00   |  100  
2021-10-29 11:04:00   |  101  
2021-10-29 09:05:00   |  99  
2021-10-29 09:03:00   |  100  

现在我想查询这些数据以对 10 分钟会话窗口内发生的行进行分组(在一定时间内没有行分隔)。

查询看起来像:

select  
  max(timestamp) as "end",  
  min(timestamp) as "start",  
  avg(value) as "average"  
from table  
group by  
  session_window(timestamp, 'interval 10 minutes') -- <- how to do this part?

输出应该是这样的:

    end               |      start            |   average

2021-10-29 11:05:00   |  2021-10-29 11:04:00  |   100.5
2021-10-29 09:05:00   |  2021-10-29 09:03:00  |   99.5

【问题讨论】:

    标签: sql postgresql time-series timescaledb


    【解决方案1】:

    如果您想根据给定记录集的最小和最大时间戳创建 10 分钟的时间间隔,您可能首先使用 tsrange 在子查询或 CTE 中建立这些窗口,最后在外部查询中聚合记录与这些新窗口重叠,例如

    CREATE TABLE iot (tmst timestamp without time zone, value numeric);
    
    INSERT INTO iot VALUES 
    ('2021-10-29 11:05:00',100),
    ('2021-10-29 11:04:00',101),
    ('2021-10-29 09:05:00',99),  
    ('2021-10-29 09:03:00',100);
    
    WITH j AS (
      SELECT tsrange(series,series+'00:09:59'::time) AS ts_win 
      FROM (SELECT generate_series(min(tmst),max(tmst),interval '10 minutes') 
            FROM iot) i (series)  
    ) 
    SELECT 
     lower(j.ts_win) AS start, upper(j.ts_win) AS end, avg(iot.value) AS average
    FROM j
    JOIN iot ON j.ts_win @> iot.tmst
    GROUP BY j.ts_win;
    
            start        |         end         |       average        
    ---------------------+---------------------+----------------------
     2021-10-29 09:03:00 | 2021-10-29 09:12:59 |  99.5000000000000000
     2021-10-29 11:03:00 | 2021-10-29 11:12:59 | 100.5000000000000000
    (2 Zeilen)
    

    【讨论】:

      【解决方案2】:

      我们首先需要将事件时间戳放入 10 分钟长的时隙 (tp),然后按 tp 分组并聚合。在 Postgresql 14 中,您可以使用 date_bin 函数来执行此操作。在版本 14 之前使用函数 date_bin 下面是 PG14 函数的模拟,具有相同的返回值和参数的签名。

      create or replace function date_bin
       (trunc_period interval, ts timestamptz, base_ts timestamptz default '1970-01-01Z')
       returns timestamptz language sql immutable as
      $$
      select
        base_ts
        + floor(extract(epoch from ts - base_ts) / extract(epoch from trunc_period))::bigint
        * trunc_period;
      $$;
      
      CREATE temporary TABLE t (ts timestamp, val int);
      INSERT INTO t (ts, val)
      VALUES
          ('2021-10-29 11:05:00', 100),
          ('2021-10-29 11:04:00', 101),
          ('2021-10-29 09:05:00', 99),
          ('2021-10-29 09:03:00', 100);
      
      with cte as 
      (
          select date_bin(interval '10 minutes', ts) tp, val 
          from t
      )
      select tp "start", tp + interval '10 minutes' "end", avg(val) average
      from cte
      group by tp;
      

      或者如您示例中的查询所示:

      select  
        max(ts) as "end", min(ts) as "start", avg(val) as "average"  
      from t  
      group by date_bin(interval '10 minutes', ts);
       
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2022-12-05
        • 1970-01-01
        • 2016-06-13
        • 1970-01-01
        • 2015-01-03
        • 2019-01-30
        • 2016-04-02
        • 2010-09-30
        相关资源
        最近更新 更多