timescaledb/postgres 中基于时间的分组 window 函数

Time-based grouped window functions in timescaledb/postgres

我在名为 prices 的 timescaledb 中有一个 hypertable,如下所示:

ts 仪器编号
08:00:01 一个 100
08:00:01 B 200
08:00:02 B 205
08:00:04 一个 95
08:00:06 C 300
08:00:07 一个 90

其中每一行是给定仪器 (instrumentId) 在给定时间戳 (ts) 的测量值 (value)。每个仪器在不同的时间进入,我不能保证我会在给定的时间戳获得给定仪器的数据点。

我的 hypertable 使用时间戳列作为我的时间索引,对于其他上下文,我有大约 2 亿行跨越数十种仪器的数年亚秒级数据。

我希望能够为我拥有的每个测量的每个仪器生成 5 秒未加权的历史平均值。对于我的 table 中的每一行,我希望能够找到所有具有相同仪器的行,并且在 (row_timestamp, row_timestamp - 5 seconds) 之间有一个时间戳(这个集合将包括我们正在生成的行的数据)。然后我想取平均值,但我也想从中获取其他统计数据(stdev、sum 等)是合理的。

上述示例 table 的查询输出如下所示:

ts 仪器编号 avg_5s_window_value
08:00:01 一个 100
08:00:01 B 200
08:00:02 B 202.5
08:00:04 一个 97.5
08:00:06 C 300
08:00:07 一个 92.5

我可以通过将 table 加入自身来在小批量数据中实现这一点,但这是一个非常低效的解决方案,而且我知道实现这一点的正确方法是在兜帽。对此的查询如下所示:

WITH lhs AS (
    SELECT 
        ts, instrumentId, value, rank() OVER (ORDER BY ts)
    FROM prices
),
splay AS (
    SELECT
        lhs.instrumentId, lhs.ts, lhs.rank, rhs.value
    FROM lhs as rhs
    JOIN lhs
    ON 
        lhs.instrumentId=rhs.instrumentId
        AND rhs.ts BETWEEN lhs.ts - INTERVAL '5s' AND lhs.ts
    ORDER BY lhs.instrumentId, lhs.rank, rhs.rank
)
SELECT
    MAX(instrumentId), min(ts), AVG(value)
FROM splay
GROUP BY rank;

对于一天的数据,以上 运行 需要大约 2 分钟,但我可以在大约 45 秒内对 1 年的数据执行 pandas 中的操作,所以我相信在 SQL.

中有更好的方法

如何在 timescaledb/postgres 中实现高效的分组 window 函数?

我想你要找的是 time_bucket 和分组依据,比如:

SELECT time_bucket('5s'::interval, ts), instrumentID, avg(value)
FROM prices
GROUP BY time_bucket('5s'::interval, ts), instrumentID;

这将在范围的开头按时间戳分组,如果你想要结束,你可以向它添加'5s'...

如果您尝试对原始数据集中的每一行执行此操作,那么您将使用 window 函数和 partition by 子句和范围子句,如下所示:

SELECT *, avg(value) OVER (PARTITION BY instrumentId ORDER BY ts RANGE '5s' PRECEDING)
FROM prices;