返回

文章详情

期待 Postgres 19: 终于到了时候

Hacker News2026年6月12日 16:44

最近,数据库领域出现了一种新类型的问题:这些数据在上周二是什么样的?也许这是某个产品在假期促销开始之前的价格,或者某位员工在那场没有人要求的重组之前所属的部门。在不引入整个审计触发系统的情况下,我们如何知道在那确切日期变化之前和之后的数据是什么样的?SQL:2011 标准在十多年前通过时间表格式化了一个合适的解决方案。其他数据库引擎相对较快地采用了其中的一部分。特征上,Postgres 花了时间。但 Postgres 19 终于为这个聚会带来了原生的时间表支持 — 这次等待绝对值得。让我们看看我们正在使用的东西。旧式方式 在我们走到那些闪亮的新东西之前,让我们看看这个古老的方法以获取一些视角。假设我们想要跟踪产品价格随时间的变化。一个合理的第一次尝试可能看起来像这样:CREATE EXTENSION IF NOT EXISTS btree_gist; CREATE TABLE products ( product_id INT NOT NULL, product_name TEXT NOT NULL, price NUMERIC(10,2) NOT NULL, valid_from DATE NOT NULL, valid_to DATE NOT NULL, CONSTRAINT no_time_travel CHECK (valid_from < valid_to) ); 简单吧。我们有一个产品,一个价格,以及价格的有效日期范围。不幸的是,没有什么能阻止我们为同一产品插入两个重叠日期范围的行。产品编号 42 在同一个星期二可能是 9.99 美元和 14.99 美元。一旦发现这一点,你的会计可能会有一些挑剔的话。这里的传统 Postgres 答案是 btree_gist 扩展和一个排除约束:ALTER TABLE products ADD CONSTRAINT no_overlapping_prices EXCLUDE USING gist ( product_id WITH =, daterange(valid_from, valid_to) WITH && ); 这有效。如果我们尝试插入一行冲突的记录,Postgres 会捕获:INSERT INTO products VALUES (1, 'Widget', 9.99, '2025-01-01', '2025-07-01'); INSERT INTO products VALUES (1, 'Widget', 12.99, '2025-06-01', '2026-01-01'); ERROR: conflicting key value violates exclusion constraint "no_overlapping_prices" 通过使用 btree_gist 解决了一个问题!那么问题出在哪里呢?好吧,有几点:每个人都知道 BTREE 和一般索引,但 GiST 是特定于 Postgres 的,因此需要经验来理解。对于一个可选扩展来说,情况更是如此。排除约束语法相当不直观。它在文档中有说明,但没有理由大家会认为这是一种标准方法。表本身没有时间的意识。基本上,Postgres 不理解这是时间数据。这仅仅是列和一个使用花式索引类型的深奥约束。每次改变时间范围的更新都需要手动分割和缝合行,这意味着应用程序必须承载时间正确性的全部负担。这是最基本的——坦白说我们可以做得更好。时间简史 对 Postgres 来说渴望适当的时间支持并不新鲜。SQL:2011 标准引入了 APPLICATION TIME 期间、WITHOUT OVERLAPS 约束和 FOR PORTION OF 语法用于时间数据操作。2011 年已经是很久以前的事了。Henrietta Dombrovskaya(给朋友们的昵称是 Hetti)是 Postgres 生态系统中时间数据最早的倡导者之一。她与 Chad Slaughter 开发了 pg_bitemporal 扩展。这是一个在 Postgres 中使用 PL/pgSQL 完全管理双时间表的框架。自 2015 年以来,她在多个会议上展示了这些概念,演示了如何同时跟踪有效时间(这一事实在现实世界中何时有效?)和事务时间(数据库何时记录这一事实?)。这种区分很重要。有效时间表示“这个价格从一月到六月有效。”事务时间是数据库的视角,表示“这一行是在 3 月 12 日下午 3:47 插入的,并在 4 月 3 日上午 9:01 被取代。”结合这两者会产生一个双时间表,可以回答类似“上周二我们认为价格是多少,基于我们当时所知?”这样的问题。pg_bitemporal 方法在构建上很大程度上依赖于我们之前讨论的同样的 EXCLUDE USING gist 机制,但进行了双重处理:一个用于有效范围(有效时间),另一个用于断言范围(事务时间)。表定义看起来像这样:CREATE TABLE bi_temporal.customers ( cust_nbr INTEGER, cust_nm TEXT, cust_type TEXT, effective_range TSTZRANGE, asserted_range TSTZRANGE, row_created_at TIMESTAMPTZ, EXCLUDE USING gist ( cust_nbr WITH =, effective_range WITH &&, asserted_range WITH && ) ); 这是由单个排除约束强制的两个时间维度。该扩展还引入了双时间插入、更新、修正、非活跃和删除函数,以及用于时间推理的 Allen 关系的实现。这是在 Postgres 当时所提供的基础上构建了许多机制。它运作良好!但扩展可以只...

赞助内容

NordVPN Next-gen Antivirus

本站免费、广告极少。如果觉得有帮助,可以请我们喝杯咖啡 —— 任何金额都对持续运营有实际帮助。

请我喝杯咖啡