Postgres 18:RETURNING子句中的OLD和NEW行

Postgres 18 今日发布。在异步I/O和UUIDv7支持等头条功能下方,我们发现了这项实用的小改进:

本次发布新增功能:在INSERT、UPDATE、DELETE和MERGE命令的RETURNING子句中,可同时访问旧值(OLD)和新值(NEW)。

虽然不像异步I/O那样具有颠覆性,但这确实是那些在特定场景下不可或缺的小功能之一。

以下通过UPDATE操作获取所有新旧值的简易演示:

UPDATE fruit
SET quantity = 300
WHERE item = 'Apples'
RETURNING OLD.*, NEW.*;

 id |  item  | quantity | id |  item  | quantity
----+--------+----------+----+--------+----------
  5 | Apples |      200 |  5 | Apples |      300
(1 row)

利用OLD检测更新插入操作中的新行

元素周期表

假设我们正在执行upsert操作,需要区分RETURNING返回的行是新插入还是更新的现有行。虽然以前也能实现,但依赖于对xmax = 0的反直觉检查(见下文最后一行):

INSERT INTO webhook (
    id,
    data
) VALUES (
    @id,
    @data
)
ON CONFLICT (id)
    DO UPDATE SET id = webhook.id -- force upsert to return a row
RETURNING webhook.*,
    (xmax = 0) AS is_new;

该语句依赖于Postgres锁机制实现的特性——新插入行会将xmax设为零(详见完整原理说明)。虽然有效,但并非API的保证行为,随时可能变更。

在 Postgres 18 中,我们可以重构上述实现,使其更易于理解且不依赖具体实现细节。实现也很简单——只需在返回子句中检查 OLD 是否为空:

INSERT INTO webhook (
    id,
    data
) VALUES (
    @id,
    @data
)
ON CONFLICT (id)
    DO UPDATE SET id = webhook.id -- force upsert to return a row
RETURNING webhook.*,
    (OLD IS NULL)::boolean AS is_new;

访问 OLDNEW 无疑还有许多其他实用场景,但这个示例能让我们立即改进 18 版之前的代码。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注


京ICP备12002735号