开发者生态
evening
期待 Postgres 19:是时候了
2026-06-12
1 阅读
xngbuilds
< 博客首页 期待 Postgres 19:Shaun Thomas | 是时候了2026 年 6 月 12 日 最近,数据库领域出现了一种新型问题:上周二的数据是什么样的?也许是假期促销开始前产品的价格,或者是在没有人要求的重组之前员工属于哪个部门。如果不添加整个审计触发系统,我们如何知道在该确切日期更改之前和之后的数据是什么样的? SQL:2011 标准十多年前就通过时态表形式化了正确的解决方案。其他数据库引擎相对较快地采用了它的一部分。 Postgres 的特点是从容不迫。但 Postgres 19 终于为聚会带来了原生时态表支持——而且这是非常值得等待的。让我们看看我们正在做什么。老式的方法在我们开始讨论闪亮的新东西之前,让我们先看看硬邦邦的旧方法以获得一些观点。假设我们想要跟踪一段时间内的产品定价。合理的第一次尝试可能如下所示: CREATE EXTENSION IF NOT EXISTS btree_gist;创建表产品(product_id INT NOT NULL,product_name TEXT NOT NULL,价格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_idWITH =, daterange(valid_from, valid_to)WITH && );这有效。如果我们尝试插入冲突的行,Postgres 会捕获它: INSERT INTO products VALUES (1, 'Widget', 9.99, '2025-01-01', '2025-07-01');插入产品值(1、“小部件”、12.99、“2025-06-01”、“2026-01-01”);错误:冲突的键值违反排除约束“no_overlapping_prices”使用 btree_gist 解决了问题!那么问题出在哪里呢?好吧,有几件事:每个人都知道 BTREE 和一般索引,但 GiST 特定于 Postgres,因此需要经验才能理解。作为可选扩展,这会加倍。排除约束语法非常不直观。它在文档中,但没有理由有人会认为这是标准方法。桌子本身没有时间意识。基本上,Postgres 不理解这是时间数据。它只是列和使用奇特索引类型的深奥约束。每次更改时间范围的更新都需要手动拆分和缝合行,这意味着应用程序必须承担时间正确性的全部负担。这是最低限度的要求,坦率地说,我们可以做得更好。时间简史 在 Postgres 中寻求适当的时间支持并不新鲜。 SQL:2011 标准引入了 APPLICATION TIME 周期、WITHOUT OVERLAPS 约束以及时态 DML 的 FOR PORTION OF 语法。 2011年已经过去很久了。 Henrietta Dombrovskaya(她的朋友们称为 Hetti)是 Postgres 生态系统中时态数据最早的拥护者之一。她与 Chad Slaughter 一起开发了 pg_bitemporal 扩展。它是一个使用 PL/pgSQL 完全在 Postgres 内管理双时态表的框架。自 2015 年以来,她在多个会议上提出了这些概念,演示了如何同时跟踪有效时间(这个事实在现实世界中何时成立?)和交易时间(数据库何时记录这个事实?)。区别很重要。有效时间显示“此价格从一月到六月有效”。事务时间是数据库的角度,表示“该行于 3 月 12 日下午 3:47 插入,并于 4 月 3 日上午 9:01 取代”。将两者结合起来会产生一个双时态表,它可以回答诸如“根据我们当时的了解,我们认为上周二的价格是多少?”之类的问题。 pg_bitemporal 方法在很大程度上依赖于我们之前讨论的相同 EXCLUDE USING 要点机制,但加倍了:一个排除有效范围(有效时间),另一个排除断言范围(交易时间)。表定义如下所示: 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_nbrWITH =, effective_rangeWITH &&,asserted_rangeWITH && ) );这是由单个排除约束强制执行的两个时间维度。该扩展还引入了双时态插入、更新、更正、失活和删除的函数,以及用于时间推理的 Allen 区间关系的实现。这是很多机器