使用 postgres、timescaledb 获取时间戳至少在 5 分钟前的最新行
Posted
技术标签:
【中文标题】使用 postgres、timescaledb 获取时间戳至少在 5 分钟前的最新行【英文标题】:Get most recent row whose timestamp is at least 5 minutes ago using postgres, timescaledb 【发布时间】:2022-01-15 00:34:27 【问题描述】:我遇到了一些我认为 timescaledb
可以提供帮助的问题。
假设我有这张桌子:
CREATE TABLE purchase
(
id integer NOT NULL DEFAULT nextval('purchase_id_seq'::regclass),
"timestamp" timestamp without time zone NOT NULL,
country character varying(128) COLLATE pg_catalog."default",
product character varying(128) COLLATE pg_catalog."default",
quantity numeric(64,32),
price numeric(64,32)
)
代表购买:
身份证 购买时间戳 发生的国家 正在购买的产品 购买数量 按 1 数量支付的价格对于每次购买,我要计算(伪代码):
price - last price for given (country, product) where timestamp - timestamp of old record > 5 minutes
例如,如果我有这些购买:
id timestamp country product quantity price
1 2021-12-09 07:12:11.13 US apple 1 1.2
2 2021-12-09 07:13:11.13 US apple 2 1.3
3 2021-12-09 07:19:12.13 US apple 2 1.4
4 2021-12-09 07:20:19.13 US apple 2 0.9
然后我会有这些增量
id timestamp country product quantity price last_price_at_least_five_minutes_ago
1 2021-12-09 07:12:11.13 US apple 1 1.2 NULL
2 2021-12-09 07:13:11.13 US apple 2 1.3 NULL
3 2021-12-09 07:17:12.13 US apple 2 1.4 1.2
4 2021-12-09 07:20:19.13 US apple 2 0.9 1.3
对于每个 CURRENTROW,询问“具有小于 CURRENTROW 的最高时间戳的行的价格 - '5 分钟' 的最简单方法是什么?
我愚蠢地尝试了这个:
SELECT
t1.country,
t1.product,
t1.timestamp,
t1.id,
t1.price,
t2.id AS last_id,
t2.timestamp AS last_timestamp,
t2.price AS last_price
FROM
purchase t1
LEFT JOIN purchase t2
ON
t2.timestamp < t1.timestamp - INTERVAL '5m' AND
t1.country = t2.country AND
t1.product = t2.product
GROUP BY
t1.country,
t1.product,
t1.id,
t1.price,
t1.timestamp,
t2.id,
t2.price,
t2.timestamp
但这会挂起,我确信它做了很多不必要的工作,因为连接的结果集很大(每行 * N,其中 N 是 5m 前的行数)
【问题讨论】:
【参考方案1】:您也可以使用窗口函数,尽管您必须确定较大数据集上的任何性能指标以及您要查询的范围。
此示例选择分区中的最后一个值(在本例中为按产品),按时间排序 ASC。为了获得至少 5 分钟前的值,您必须设置一个开始时间超过 5 分钟(这使用 UNBOUNDED PRECEDING
)和 5 分钟前('5 minutes'::interval PRECEDING
)的窗口范围。
使用UNBOUNDED PRECEDING
最终可能会很昂贵,具体取决于您拥有的行数,因此如果您知道/期望您的数据具有某种规律性(例如range between '20 minute'::interval PRECEDING and '5 minute'::interval PRECEDING
),您可以设置某种开始间隔
select ts, country, product,quantity,price,
last_value(purchase.price) over w as last_price_at_least_five_minutes_ago
from purchase
window w as (partition by product order by ts range between unbounded PRECEDING and '5 minutes'::interval PRECEDING)
order by ts asc;
结果:
ts |country|product|quantity|price|last_price_at_least_five_minutes_ago|
-----------------------+-------+-------+--------+-----+------------------------------------+
2021-12-09 07:12:11.130|US |apple | 1.0| 1.2| |
2021-12-09 07:13:11.130|US |apple | 2.0| 1.3| |
2021-12-09 07:17:12.130|US |apple | 2.0| 1.4| 1.2|
2021-12-09 07:20:19.130|US |apple | 2.0| 0.9| 1.3|
【讨论】:
【参考方案2】:您可以使用标量子查询,字面意思是“具有小于 CURRENTROW 的最高时间戳的行的价格 - 5 分钟”。
select *,
(
select price
from purchase
where product = currentrow.product -- more conditions can be added here
and "timestamp" < currentrow."timestamp" - interval '5 minutes' -- "less than CURRENTROW - 5 minutes"
order by "timestamp" desc limit 1 -- "the highest timestamp"
) as last_price_at_least_five_minutes_ago
from purchase as currentrow;
currentrow
不是purchase
表的足够好别名,但很适合子查询的逻辑。
【讨论】:
以上是关于使用 postgres、timescaledb 获取时间戳至少在 5 分钟前的最新行的主要内容,如果未能解决你的问题,请参考以下文章
Centos7 安装 PostgreSql 14 数据库 和 timescaledb 时序库
问题:如何在具有卷的ARM体系结构上在Docker上运行TimeScaleDB?
如何使用 JPQL、Spring Data Repositories 和 Hibernate 为 TimescaleDB `time_bucket` 函数参数化 Postgresql 间隔