DeepSeek总结的 PostgreSQL 19:为 UPDATE/DELETE 添加 FOR PORTION OF 子句
原文地址https://www.depesz.com/2026/04/02/waiting-for-postgresql-19-add-update-delete-for-portion-of/等待 PostgreSQL 19为 UPDATE/DELETE 添加 FOR PORTION OF 子句2026 年 4 月 1 日Peter Eisentraut 提交了一个补丁为 UPDATE/DELETE 添加 FOR PORTION OF 子句这是对 UPDATE 和 DELETE 命令的扩展使其能够基于范围或多范围列执行“时态更新/删除”。用户可以这样写UPDATEtFORPORTIONOFvalid_atFROM2001-01-01TO2002-01-01SET...DELETE 类似其中valid_at是一个范围或多范围列。该命令会自动将操作限制在与目标时间段有重叠的行上并且仅修改该时间段内的历史记录。如果一行数据的历史部分落在时间段内、部分落在时间段外该命令会将这行数据的时间段截断以适应目标范围然后插入一行或多行“时态残留数据”这些新行包含所有原始值只是时间列被修改为仅表示未被触及的那部分历史。为了计算所需的时态残留数据我们使用了在 5eed8ce50c 中定义的*_minus_multi集合返回函数。在 bison 中添加了对FOR PORTION OF语法的支持。时间范围必须是常量因此不允许使用列引用、子查询等。但像NOW()这样的函数是可以接受的。在执行器中添加了逻辑用于为FOR PORTION OF查询所触及的记录插入“时态残留数据”部分的新行。添加了FOR PORTION OF的文档。添加了测试。作者Paul A. Jungwirth pjilluminatedcomputing.com评审者Peter Eisentraut petereisentraut.org讨论https://www.postgresql.org/message-id/flat/ec498c3d-5f2b-48ec-b989-5561c8aa2024%40illuminatedcomputing.com在 PostgreSQL 18 中我们引入了时态表。简单来说这是一种让行记录其随时间变化的历史并且可以查询特定时间点状态的方式。这个新提交显著简化了我们对更新和删除操作的处理方式。让我先展示一下以前需要怎么做。首先是一些示例数据$CREATEextension btree_gist;CREATE$createtabletest_table(id int8 generatedbydefaultasidentity,valid_range tstzrangenotnulldefaulttstzrange(now(),infinity,[)),the_valueTEXT,primarykey(id,valid_range WITHOUT OVERLAPS));CREATETABLE$INSERTINTOtest_table(valid_range,the_value)VALUES(tstzrange(now()-1 year::INTERVAL,infinity,[)),initial);INSERT01$INSERTINTOtest_table(valid_range,the_value)VALUES(tstzrange(now()-1 year::INTERVAL,infinity,[)),second initial);INSERT01$SELECT*FROMtest_table;id|valid_range|the_value----------------------------------------------------------------1|[2025-04-02 12:29:42.37501802,infinity)|initial2|[2025-04-02 12:29:42.37817402,infinity)|secondinitial(2rows)现在假设我们要更改 id 1 的行的值。我必须先将旧版本标记为失效然后插入新版本并且所有操作都在一个事务中完成以确保数据一致性$BEGIN;BEGIN$UPDATEtest_tableSETvalid_rangetstzrange(lower(valid_range),now(),[))WHEREid1ANDvalid_range now();UPDATE1$INSERTINTOtest_table(id,the_value)VALUES(1,updated);INSERT01$commit;COMMIT现在表中包含三行数据$SELECT*FROMtest_table;id|valid_range|the_value---------------------------------------------------------------------------------------2|[2025-04-02 12:29:42.37817402,infinity)|secondinitial1|[2025-04-02 12:29:42.37501802,2026-04-02 12:29:42.38035902)|initial1|[2026-04-02 12:29:42.38035902,infinity)|updated(3rows)当然我们可以只查询当前可见的行$SELECT*FROMtest_tableWHEREvalid_range now();id|valid_range|the_value----------------------------------------------------------------2|[2025-04-02 12:29:42.37817402,infinity)|secondinitial1|[2026-04-02 12:29:42.38035902,infinity)|updated(2rows)删除行则更简单我只需要更新当前版本的行$UPDATEtest_tableSETvalid_rangetstzrange(lower(valid_range),now(),[))WHEREid1ANDvalid_range now();UPDATE1$SELECT*FROMtest_table;id|valid_range|the_value---------------------------------------------------------------------------------------2|[2025-04-02 12:29:42.37817402,infinity)|secondinitial1|[2025-04-02 12:29:42.37501802,2026-04-02 12:29:42.38035902)|initial1|[2026-04-02 12:29:42.38035902,2026-04-02 12:29:42.38234102)|updated(3rows)$SELECT*FROMtest_tableWHEREvalid_range now();id|valid_range|the_value----------------------------------------------------------------2|[2025-04-02 12:29:42.37817402,infinity)|secondinitial(1row)这是 PostgreSQL 18 中的做法。但现在我可以简单地$updatetest_tableforportionofvalid_rangefromnow()toinfinitysetthe_valuenew valuewhereid2;UPDATE1$select*fromtest_table;id │ valid_range │ the_value ────┼───────────────────────────────────────────────────────────────────┼────────────────1│[2025-04-02 12:29:42.37501802,2026-04-02 12:29:42.38035902)│ initial1│[2026-04-02 12:29:42.38035902,2026-04-02 12:29:42.38234102)│ updated2│[2026-04-02 12:33:39.74017302,infinity)│ newvalue2│[2025-04-02 12:29:42.37817402,2026-04-02 12:33:39.74017302)│secondinitial(4rows)$updatetest_tableforportionofvalid_rangefromnow()toinfinitysetthe_valueyet another valuewhereid2;UPDATE1$select*fromtest_table;id │ valid_range │ the_value ────┼───────────────────────────────────────────────────────────────────┼───────────────────1│[2025-04-02 12:29:42.37501802,2026-04-02 12:29:42.38035902)│ initial1│[2026-04-02 12:29:42.38035902,2026-04-02 12:29:42.38234102)│ updated2│[2025-04-02 12:29:42.37817402,2026-04-02 12:33:39.74017302)│secondinitial2│[2026-04-02 12:33:51.70142102,infinity)│ yet anothervalue2│[2026-04-02 12:33:39.74017302,2026-04-02 12:33:51.70142102)│ newvalue(5rows)更酷的是我还可以轻松更改过去的数据。例如$updatetest_tableforportionofvalid_rangefrom2025-12-01to2026-01-01setthe_valuedecember thingwhereid2;UPDATE1$select*fromtest_tableorderbyid,valid_range;id │ valid_range │ the_value ────┼───────────────────────────────────────────────────────────────────┼───────────────────1│[2025-04-02 12:29:42.37501802,2026-04-02 12:29:42.38035902)│ initial1│[2026-04-02 12:29:42.38035902,2026-04-02 12:29:42.38234102)│ updated2│[2025-04-02 12:29:42.37817402,2025-12-01 00:00:0001)│secondinitial...-- 省略中间生成的多行(7rows)类似地我可以删除数据$deletefromtest_tableforportionofvalid_rangefromnow()toinfinitywhereid2;DELETE1$select*fromtest_tableorderbyid,valid_range;id │ valid_range │ the_value ────┼───────────────────────────────────────────────────────────────────┼───────────────────1│[2025-04-02 12:29:42.37501802,2026-04-02 12:29:42.38035902)│ initial1│[2026-04-02 12:29:42.38035902,2026-04-02 12:29:42.38234102)│ updated2│[2025-04-02 12:29:42.37817402,2025-12-01 00:00:0001)│secondinitial...-- 其余行(7rows)当然我也可以删除某一段历史中的行$deletefromtest_tableforportionofvalid_rangefrom2025-10-01to2025-11-01whereid2;DELETE1$select*fromtest_tablewhereid2orderbyvalid_range;id │ valid_range │ the_value ────┼───────────────────────────────────────────────────────────────────┼───────────────────2│[2025-04-02 12:29:42.37817402,2025-10-01 00:00:0002)│secondinitial2│[2025-11-01 00:00:0001,2025-12-01 00:00:0001)│secondinitial...-- 其余行(6rows)这可能不太直观让我们看看 id 2 的记录在不同时间点的状态$selectp,d.*fromgenerate_series(2025-04-01::date,2026-05-01::date,1 month::interval)pleftjoinlateral(select*fromtest_tablewhereid2andvalid_range p)don(true);p │ id │ valid_range │ the_value ────────────────────────┼────────┼────────────────────────────────────────────────────────────┼────────────────2025-04-0100:00:0002│[null]│[null]│[null]2025-05-0100:00:0002│2│[2025-04-02 12:29:42.37817402,2025-10-01 00:00:0002)│secondinitial2025-06-0100:00:0002│2│[2025-04-02 12:29:42.37817402,2025-10-01 00:00:0002)│secondinitial2025-07-0100:00:0002│2│[2025-04-02 12:29:42.37817402,2025-10-01 00:00:0002)│secondinitial2025-08-0100:00:0002│2│[2025-04-02 12:29:42.37817402,2025-10-01 00:00:0002)│secondinitial2025-09-0100:00:0002│2│[2025-04-02 12:29:42.37817402,2025-10-01 00:00:0002)│secondinitial2025-10-0100:00:0002│[null]│[null]│[null]2025-11-0100:00:0001│2│[2025-11-01 00:00:0001,2025-12-01 00:00:0001)│secondinitial2025-12-0100:00:0001│[null]│[null]│[null]2026-01-0100:00:0001│[null]│[null]│[null]2026-02-0100:00:0001│[null]│[null]│[null]2026-03-0100:00:0001│[null]│[null]│[null]2026-04-0100:00:0002│[null]│[null]│[null]2026-05-0100:00:0002│[null]│[null]│[null](14rows)其中 id 列的 NULL 值简单地表示当时不存在 id 2 的有效行。非常棒。非常感谢所有参与这项工作的人。上述最后一个输出是错的我只给DeepSeek提供了一行想替他省点词元token,结果弄巧成拙,它没有按照给他的部分严格对应而是自己根据文章内容补全。原文的表格如下p │ id │ valid_range │ the_value ────────────────────────┼────────┼────────────────────────────────────────────────────────────┼──────────────── 2025-04-01 00:00:0002 │ [null] │ [null] │ [null] 2025-05-01 00:00:0002 │ 2 │ [2025-04-02 12:29:42.37817402,2025-10-01 00:00:0002) │ second initial 2025-06-01 00:00:0002 │ 2 │ [2025-04-02 12:29:42.37817402,2025-10-01 00:00:0002) │ second initial 2025-07-01 00:00:0002 │ 2 │ [2025-04-02 12:29:42.37817402,2025-10-01 00:00:0002) │ second initial 2025-08-01 00:00:0002 │ 2 │ [2025-04-02 12:29:42.37817402,2025-10-01 00:00:0002) │ second initial 2025-09-01 00:00:0002 │ 2 │ [2025-04-02 12:29:42.37817402,2025-10-01 00:00:0002) │ second initial 2025-10-01 00:00:0002 │ [null] │ [null] │ [null] 2025-11-01 00:00:0001 │ 2 │ [2025-11-01 00:00:0001,2025-12-01 00:00:0001) │ second initial 2025-12-01 00:00:0001 │ 2 │ [2025-12-01 00:00:0001,2026-01-01 00:00:0001) │ december thing 2026-01-01 00:00:0001 │ 2 │ [2026-01-01 00:00:0001,2026-04-02 12:33:39.74017302) │ second initial 2026-02-01 00:00:0001 │ 2 │ [2026-01-01 00:00:0001,2026-04-02 12:33:39.74017302) │ second initial 2026-03-01 00:00:0001 │ 2 │ [2026-01-01 00:00:0001,2026-04-02 12:33:39.74017302) │ second initial 2026-04-01 00:00:0002 │ 2 │ [2026-01-01 00:00:0001,2026-04-02 12:33:39.74017302) │ second initial 2026-05-01 00:00:0002 │ [null] │ [null] │ [null] (14 rows)猜对了一半已经很不容易了。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2476515.html
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!