当前位置: 首页 > article >正文

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)猜对了一半已经很不容易了。

相关文章:

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 提交了一个补丁&#xf…...

对在aarch64 Linux环境编译安装的CinderX补充测试

前文最后说,CinderX报错不能用,这不对,我在其github存储库上提了这个issue,alexmalyshev回复 I think that’s actually just a warning that you’re getting but things should be working after that?Right, this is just a l…...

springcloud项目如何禁用三方依赖的拦截器

背景: 原始代码中有一个自定义的通用依赖,这个依赖中有很多通用方法和拦截器供整个系统使用。 需求: 禁用其中一个拦截器,保留其他方法和拦截器,过滤器等。 拦截器介绍 原有拦截器,自己封装了一个jdk&#…...

如何查看浏览器中当前存储的 Cookie?

如何查看浏览器中的 Cookie?为什么有些 Cookie 看不到?1. 引言:快递单号与隐私信封2. Cookie 是什么?(小白必备)3. 核心问题:为什么有些 Cookie“看不到”?4. 如何查看 Cookie&#…...

如何保证 Session ID 的随机性和不可猜测性?

你的 Session ID 安全吗?—— 从可预测的“门禁卡”到安全的“加密钥匙”1. 引言:一张编号可以被猜到的门禁卡2. Session 与 Session ID:会话的“钥匙”3. 为什么 Session ID 必须随机且不可预测?4. 攻击详解:会话劫持…...

OpenClaw安全防护:Phi-3-mini操作权限管控方案

OpenClaw安全防护:Phi-3-mini操作权限管控方案 1. 为什么需要OpenClaw安全防护 上周我在调试一个自动化文档整理任务时,差点酿成大错。当时OpenClaw连接的Phi-3-mini模型误解了我的指令,试图删除整个工作目录下的文件。虽然及时终止了进程&…...

容器环境下各种兼容模式+多实例

注意: #多实例端口不同数据目录不同容器名不同 1. -p 主机端口:容器端口 容器端口永远是 54321(不用改) 主机端口必须不一样:4321、4322、4323... 一个端口只能给一个数据库用,就像一个门不能同时进两个人。2. -v 主机…...

10. Doris 系列第10篇:数据查询全攻略|Join/子查询/窗口函数,从基础到高级实战

适合人群:大数据开发、Doris查询调优工程师、数仓分析师、BI工程师核心价值:吃透Doris 2.x数据查询核心能力,掌握Join算法选型、子查询优化、多维聚合、窗口函数实战,解决查询慢、资源浪费、语法报错等问题系列说明:本…...

从package.xml到CMakeLists.txt:手把手教你配置一个ROS1机器人控制包(附完整项目模板)

从package.xml到CMakeLists.txt:构建工业级ROS1机器人控制包的完整指南 在机器人操作系统(ROS)开发中,功能包的配置质量直接影响项目的可维护性和扩展性。本文将带您深入理解ROS1功能包的核心配置文件,通过一个完整的工业机器人控制包案例&am…...

告别上位机!纯FPGA实现exFAT文件系统,让你的高速数据直接存成标准文件

纯FPGA实现exFAT文件系统:硬件工程师的高速存储革命 在高速数据采集领域,从雷达信号处理到卫星通信,工程师们长期面临一个核心痛点:如何将海量原始数据高效、可靠地转换为标准文件格式。传统方案依赖上位机或嵌入式处理器进行文件…...

OpenCV透视变换实战:从文档矫正到AR应用

1. 透视变换基础:从原理到生活场景 想象一下你正在用手机拍摄一张放在桌上的发票,由于角度问题,发票在照片里变成了梯形。这时候你需要的正是透视变换——它能把这个梯形"掰正"成规整的矩形。在计算机视觉领域,透视变换…...

Apollo6.0 Lattice算法实战解析——从轨迹组合到最优路径生成

1. Lattice算法在Apollo6.0中的核心作用 Lattice算法是Apollo自动驾驶系统中的关键路径规划模块,它负责将横向和纵向轨迹进行智能组合,最终生成安全、舒适且符合交通规则的最优行驶路径。这个算法就像一位经验丰富的导航员,不仅要考虑车辆当前…...

别再死磕逐位计算了!用C语言手撸一个CRC32查表函数(附完整代码和表格生成)

从零构建高性能CRC32查表算法:嵌入式场景的极致优化实践 在嵌入式开发中,数据校验的效率和资源消耗往往成为系统设计的瓶颈。传统逐位计算的CRC32实现虽然直观,但在处理高速数据流或资源受限环境时,其性能劣势暴露无遗。查表法通过…...

ComfyUI-WanVideoWrapper全栈指南:从认知到实践的AI视频生成解决方案

ComfyUI-WanVideoWrapper全栈指南:从认知到实践的AI视频生成解决方案 【免费下载链接】ComfyUI-WanVideoWrapper 项目地址: https://gitcode.com/GitHub_Trending/co/ComfyUI-WanVideoWrapper 一、认知篇:理解AI视频生成的技术基础 1.1 核心概念…...

微信聊天记录本地管理:WeChatMsg实现数据主权与记忆留存的完整方案

微信聊天记录本地管理:WeChatMsg实现数据主权与记忆留存的完整方案 【免费下载链接】WeChatMsg 提取微信聊天记录,将其导出成HTML、Word、CSV文档永久保存,对聊天记录进行分析生成年度聊天报告 项目地址: https://gitcode.com/GitHub_Trend…...

快马平台快速原型:十分钟搭建openclaw skills机器人抓取仿真环境

最近在研究机器人抓取技能(openclaw skills)的仿真验证,发现用InsCode(快马)平台可以快速搭建原型环境。整个过程比想象中简单很多,十分钟就能跑通基础功能,分享下具体实现思路: 场景搭建 先用Three.js创建…...

5分钟掌握gInk:让屏幕标注如同纸上书写的终极指南

5分钟掌握gInk:让屏幕标注如同纸上书写的终极指南 【免费下载链接】gInk An easy to use on-screen annotation software inspired by Epic Pen. 项目地址: https://gitcode.com/gh_mirrors/gi/gInk 你是否曾在远程会议中,试图在共享屏幕上圈出重…...

ai赋能开发:使用快马平台智能优化openclaw 101抓取控制算法

最近在优化一个机械臂抓取控制项目时,发现传统的手动调参和算法改进效率太低。正好尝试了InsCode(快马)平台的AI辅助开发功能,整个过程让我对智能化编程有了全新认识。下面分享用AI优化OpenClaw 101控制算法的完整经历: 原始问题分析 初始代码…...

河海大学819传热学考研复试备考资料(新能源学院·清洁能源技术专硕专用)

温馨提示:文末有联系方式【权威备考】河海大学819传热学复试专属资料包 本资料由2025届成功录取河海大学新能源学院清洁能源技术专业硕士的学长亲自整理,初试与复试综合成绩稳居前三,内容高度贴合最新考核趋势。【高效提分利器】核心资料全覆…...

灵活创建Windows安装介质:MediaCreationTool.bat的实用指南

灵活创建Windows安装介质:MediaCreationTool.bat的实用指南 【免费下载链接】MediaCreationTool.bat Universal MCT wrapper script for all Windows 10/11 versions from 1507 to 21H2! 项目地址: https://gitcode.com/gh_mirrors/me/MediaCreationTool.bat …...

别再死记硬背了!用‘减法’和‘host/any’关键字,5分钟搞定思科ACL通配符掩码配置

思科ACL通配符掩码:5分钟掌握减法计算与host/any实战技巧 刚接触思科ACL配置时,通配符掩码总是让人头疼。那些0和1的组合看似简单,实际配置时却容易出错。但你可能不知道,掌握两个核心技巧就能彻底解决这个问题——用255.255.255.…...

从0到1落地智能仓储:C#上位机+Modbus RTU实现AGV集群调度与货物自动分拣

本文是纯实战、可直接落地的智能仓储完整方案,基于C# .NET 6 + Modbus RTU/Modbus TCP + AGV调度 + 自动分拣,从零搭建一套轻量级、低成本、高可靠的智能仓储系统,适用于电商仓库、工厂原料仓、成品仓、立体库。 无废话、无虚架构,代码可直接复制运行,适合新手从0到1上手智…...

Windows平台Datax部署与初体验:从零到一的数据同步实战

1. Windows平台Datax部署全攻略 第一次在Windows上折腾Datax的经历我还记得很清楚,当时为了同步几个简单的数据表,硬是折腾了大半天。现在回头看,其实只要掌握几个关键步骤,半小时就能搞定。Datax作为阿里开源的数据同步工具&…...

旺季仓容紧张跨境卖家如何提前规划备货与入仓

决胜销售旺季:跨境卖家的备货与入仓战略指南随着全球电商购物节日益临近,无论是年末的“黑色星期五”、圣诞季,还是区域性的大促活动,一个共同的挑战悄然浮现:仓库容量告急。对于跨境卖家而言,旺季不仅是销…...

解决Ubuntu中libc6-dev:i386依赖问题的完整指南

1. 理解libc6-dev:i386依赖问题的本质 当你正在愉快地使用Ubuntu系统,突然在执行sudo apt-get upgrade时遇到一堆红色错误提示,特别是看到"libc6-dev:i386 : 依赖: libc6:i386 ( 2.31-0ubuntu9.14) 但无法安装它"这样的报错,是不是…...

Load-Use冒险避坑指南:为什么你的RISC流水线转发电路会失效?

Load-Use冒险避坑指南:为什么你的RISC流水线转发电路会失效? 在处理器设计的迷宫中,Load-Use冒险就像是一个精心设计的陷阱,等待着那些过分依赖转发电路的工程师。这种特殊的RAW(Read After Write)冒险场景…...

深入解析DW_apb_i2c与TMP75的寄存器交互:从配置到温度读取

1. 认识TMP75温度传感器与DW_apb_i2c控制器 TMP75是德州仪器(TI)推出的一款高精度数字温度传感器,采用I2C接口通信,内置12位ADC,分辨率可达0.0625C。我在多个嵌入式项目中都用过它,实测稳定性相当不错。它的…...

从内置函数到自定义算法:用 AMDP 驱动的 CDS Scalar Function 打开 ABAP CDS 的新扩展面

在很多 ABAP CDS 项目里,开发者都会遇到一个很现实的问题:系统预置函数够用,但不总是刚好够用。简单的数值换算、字符串处理、日期推导,内置能力通常已经覆盖;可一旦业务进入更复杂的区间,例如分摊比例计算、复合折扣推导、动态计费规则、评分算法封装,单纯依赖 CDS 表达…...

AI能力已经成为2026年的分水岭:2026年企业如何选对人力资源管理平台

HR SaaS系统是基于云端部署的人力资源管理软件,帮助企业实现招聘、人事、绩效、薪酬等HR业务的数字化管理。2026年的HR SaaS已经从传统的流程工具进化为AI驱动的智能管理平台,能够自动处理简历筛选、智能推荐人才、生成绩效面谈纪要等复杂任务&#xff0…...

HoRain云--Julia运算符全解析

🎬 HoRain云小助手:个人主页 🔥 个人专栏: 《Linux 系列教程》《c语言教程》 ⛺️生活的理想,就是为了理想的生活! ⛳️ 推荐 前些天发现了一个超棒的服务器购买网站,性价比超高,大内存超划算!…...