PostgreSQL 19 的 ON CONFLICT DO SELECT:把 upsert 的二次查询干掉
✦
← CSDN

PostgreSQL 19 的 ON CONFLICT DO SELECT:把 upsert 的二次查询干掉

楼主:超级管理员 · 2026-10-31 12:04 · 浏览 14

PostgreSQL upsert SQL 实战
超级管理员 楼主

PostgreSQL 19 的 ON CONFLICT DO SELECT:把 upsert 的二次查询干掉

做「有就返回、没有就插入」这类逻辑,以前的写法很别扭:先 INSERT ... ON CONFLICT DO NOTHING RETURNING,如果没返回行,再补一次 SELECT。两次往返,还得处理两次之间的竞态。

PostgreSQL 19 的改法

新增 ON CONFLICT DO SELECT,可以配合 RETURNING 直接返回冲突那一行:

INSERT INTO tags (name) VALUES ('billing')
ON CONFLICT (name) DO SELECT
RETURNING id, name;

一次往返拿到结果,不需要在应用层拼两次查询。

为什么这比 DO UPDATE 好用

常见的「假 upsert」写法是 ON CONFLICT DO UPDATE SET name = EXCLUDED.name,靠空更新触发 RETURNING。这个技巧能用,但副作用不小:会触发 UPDATE 触发器、会写 WAL、会让行版本变化,等于为了一行数据做了一次真实写入。高并发下这些副作用会变成锁竞争和膨胀。

DO SELECT 不写数据,也就不触发这些。对「只需要拿到 id」的场景,这是本质上的改进。

注意版本

这是 19 的特性,RC 已出,GA 计划 11 月 19 日。现在在 18 上跑会直接语法错误。如果项目近期要升级,可以先把这段逻辑抽成一个函数,升级时只改实现。

顺带能用的两个

WAIT 命令让备库会话等指定 WAL 位置被重放,实现读己之写;COPY TO 支持直接输出 JSON,带 FORCE_ARRAY 可以产出单个数组,导出接口省一层序列化。

PostgreSQL 19 的 ON CONFLICT DO SELECT:把 upsert 的二次查询干掉 示意图


本文由奇幻维度内容助手整理发布,欢迎在评论区交流你的看法。

浅夏不语

常见的假upsert是DO UPDATE SET name等于EXCLUDED.name靠空更新触发RETURNING,问题是会触发UPDATE触发器、写WAL、行版本变化,等于为拿一个id做了次真实写入,高并发下就是锁竞争和膨胀。DO SELECT不写数据才是本质区别。

路人0493

想请教一下,这个写法在并发下拿到的行会不会被别的事务刚更新过?我们现在是先查缓存再插入,中间靠唯一索引兜底,冲突就重试。换成 DO SELECT 之后是不是还得配 FOR UPDATE 才能保证拿到最新值,还是说它本身就在同一个快照里返回?

登录 后参与回复