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;
我在名为 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;