PG 19 要引入时间旅行表(temporal table)了?
PG 19 要引入时间旅行表(temporal table)了?
数据库的 时态表 (Temporal Table) 是一种强大的功能,它允许数据库自动跟踪和记录表中数据随时间变化的所有历史版本。
简单来说,它就像是为你的数据表内置了一台“时光机”。
🚀 什么是时态表?
在普通的数据库表中:
当你 UPDATE(更新) 一行数据时,旧的数据就被覆盖了,永远丢失了。当你 DELETE(删除) 一行数据时,这行数据就消失了。 你只知道数据现在是什么样子,却不知道它昨天或上周是什么样子。
而 时态表 解决了这个问题。它通过以下方式工作:
自动记录历史: 当你开启时态表功能时,数据库会自动创建一张“历史表” (History Table) 来“影子”你的主表(当前数据表)。 保留旧版本:
当你 UPDATE一行数据时,数据库会先将这行数据的旧版本(修改前的样子)复制到“历史表”中,然后再更新主表中的数据为新版本。当你 DELETE一行数据时,数据库不会真的彻底删除它,而是将这行数据(删除前的最后一个版本)移动到“历史表”中。
SysStartTime (或 ValidFrom) 和 SysEndTime (或 ValidTo)。SysStartTime记录了这一行数据版本何时开始有效。SysEndTime记录了这一行数据版本何时失效(即被更新或删除)。对于当前仍然有效的数据,这个值通常是一个“遥远的未来”日期(如 9999-12-31)。
💼 举例说明:员工部门变更
假设我们有一个 Employees (员工) 表,用来跟踪员工所在的部门。
场景:
T1 (1月1日): Alice 入职,分配到 "Sales" (销售) 部门。 T2 (6月15日): Alice 表现出色,被调到 "Marketing" (市场) 部门。 T3 (12月1日): Alice 离职。
1. T1: Alice 入职 (INSERT)
当 Alice 在1月1日 09:00 入职时,我们插入一条数据:
Employees (主表 - 当前数据)
EmployeesHistory (历史表)(此时历史表是空的)
注意:
SysEndTime是 9999 年,表示这条记录是当前有效的。
2. T2: Alice 换部门 (UPDATE)
在6月15日 10:30,Alice 换到了 "Marketing" 部门。我们执行一个简单的 UPDATE 语句:UPDATE Employees SET Department = 'Marketing' WHERE EmployeeID = 101;
时态表在幕后做了这些事:
归档旧版本: 把主表中 "Sales" 那条记录的 SysEndTime修改为当前时间 (6月15日 10:30),并将其移动到历史表。插入新版本: 在主表中,将数据更新为 "Marketing",并将其 SysStartTime设置为当前时间 (6月15日 10:30),SysEndTime依然是 9999 年。
现在的状态变为:
Employees (主表 - 当前数据)
EmployeesHistory (历史表)
注意看时间戳: 历史表中的记录显示,Alice 在 "Sales" 部门的有效期是从1月1日到6月15日。主表显示她现在在 "Marketing" 部门,从6月15日开始。
3. T3: Alice 离职 (DELETE)
在12月1日 17:00,Alice 离职了。我们执行:DELETE FROM Employees WHERE EmployeeID = 101;
时态表在幕后做了这些事:
归档旧版本: 把主表中 "Marketing" 那条记录的 SysEndTime修改为当前时间 (12月1日 17:00),并将其移动到历史表。清空主表: 主表中不再有 Alice 的记录(因为她当前不是在职员工)。
现在的状态变为:
Employees (主表 - 当前数据)(现在主表空了,没有 Alice 的记录)
EmployeesHistory (历史表)
🔍 "时光机"查询 (Time-Travel Query)
这才是时态表最酷的地方。现在,我们可以查询数据在过去任意时间点的状态。
查询 1:Alice 现在在哪个部门?(这是一个标准查询,只查主表)SELECT * FROM Employees WHERE EmployeeID = 101;
结果: (空) - 因为她已经离职了。
查询 2:Alice 在 2025年3月1日 在哪个部门?(我们使用时态表特有的 AS OF 语法)SELECT * FROM Employees FOR SYSTEM_TIME AS OF '2025-03-01' WHERE EmployeeID = 101;
结果:| 101 | Alice | Sales | ... | ... | (数据库会自动查找在 3月1日 时有效的那条历史记录)
查询 3:Alice 在 2025年7月1日 在哪个部门?SELECT * FROM Employees FOR SYSTEM_TIME AS OF '2025-07-01' WHERE EmployeeID = 101;
结果:| 101 | Alice | Marketing | ... | ... | (数据库找到了 6月15日 到 12月1日 之间有效的 "Marketing" 记录)
💡 为什么要使用时态表?
审计 (Auditing): 轻松跟踪“谁在什么时间修改了什么数据”。 历史报告 (Historical Reporting): 分析数据随时间的变化趋势(例如,查询“去年此时的库存总量”)。 数据恢复 (Data Recovery): 如果有人错误地更新或删除了数据,你可以轻易地查到它被修改前的样子。 合规性 (Compliance): 很多行业(如金融、医疗)法规要求必须保留数据的历史变更记录。
PostgreSQL 19 突然发了个这样的patch: "doc: Add section for temporal tables"
看完内容白高兴一场, 是应用层逻辑删除来支持的, 还以为要引入时间旅行表(temporal table)了
https://git.postgresql.org/gitweb/?p=postgresql.git;a=commit;h=e4d8a2af07f56ec2537eb8f9a16599249db62c94
doc: Add section for temporal tables
author Peter Eisentraut <[email protected]>
Wed, 5 Nov 2025 15:38:04 +0000 (16:38 +0100)
committer Peter Eisentraut <[email protected]>
Wed, 5 Nov 2025 15:38:04 +0000 (16:38 +0100)
commit e4d8a2af07f56ec2537eb8f9a16599249db62c94
tree 2045372146786da79d71663b7c2195098ec1789f tree
parent 447aae13b0305780e87cac7b0dd669db6fab3d9d commit | diff
doc: Add section for temporal tables
This section introduces temporal tables, with a focus on Application
Time (which we support) and only a brief mention of System Time (which
we don't). It covers temporal primary keys, unique constraints, and
temporal foreign keys. We will document temporal update/delete and
periods as we add those features. This commit also adds glossary entries for temporal table, application
time, and system time.
Author: Paul A. Jungwirth <[email protected]>
Discussion: https://www.postgresql.org/message-id/flat/[email protected]
doc/src/sgml/ddl.sgml diff | blob | blame | history
doc/src/sgml/glossary.sgml diff | blob | blame | history
doc/src/sgml/images/Makefile diff | blob | blame | history
doc/src/sgml/images/temporal-entities.svg [new file with mode: 0644] blob
doc/src/sgml/images/temporal-entities.txt [new file with mode: 0644] blob
doc/src/sgml/images/temporal-references.svg [new file with mode: 0644] blob
doc/src/sgml/images/temporal-references.txt [new file with mode: 0644] blob