postgresql的on conflict do update不返回新值的根本原因是未执行更新:若冲突时新旧值相等或where条件不满足,更新被跳过,returning自然不触发;必须确保更新实际发生(如设变动字段)或用影响行数判断。

PostgreSQL 的 ON CONFLICT DO UPDATE 为什么有时不返回新值?
根本原因:没写 RETURNING,或者写了但冲突时没触发更新——DO UPDATE 只在真正执行更新时才走 RETURNING 分支,而 DO NOTHING 或跳过更新的冲突则完全不返回任何行。
常见错误现象:INSERT ... ON CONFLICT DO UPDATE RETURNING * 在唯一键冲突但新旧值相等(比如 SET x = EXCLUDED.x,而当前值本来就是这个)时,PostgreSQL 实际不会执行更新语句体,也就不会触发 RETURNING。这不是 bug,是设计行为。
- 想确保总有返回,得强制触发更新:用
SET updated_at = NOW()这类总会变的字段 - 若只关心“是否插入/更新成功”,可用
RETURNING ctid或RETURNING id配合外层判断影响行数 -
EXCLUDED是虚拟表,只能在DO UPDATE子句里用,不能在WHERE条件外引用
RETURNING 能否拿到冲突前的旧值和新值?
可以,但得靠别名和显式引用。PostgreSQL 不自动提供 “old” / “new” 上下文,必须手动从目标表和 EXCLUDED 中分别取。
使用场景:审计日志、条件计算(比如只在数值变大时更新)、避免二次查询。
- 旧值直接写表名或别名:
SELECT t.name FROM mytable t WHERE ...,但在RETURNING里直接写mytable.name就行 - 新值(即要插入的那条)必须通过
EXCLUDED.name访问 - 注意别名冲突:如果给表起了别名(如
mytable AS t),RETURNING中仍要用原始表名或EXCLUDED,不能用t.name - 示例:
RETURNING mytable.id, mytable.version AS old_version, EXCLUDED.version AS new_version
带 WHERE 条件的 DO UPDATE 对 RETURNING 有什么影响?
关键点:只有满足 WHERE 条件并实际执行了更新的行,才会出现在 RETURNING 结果中;否则该冲突行被跳过,不返回任何东西。
性能影响:带 WHERE 的更新会多一次行锁 + 检查,但不会额外扫描——它发生在已定位到冲突行之后。
- 常见陷阱:写
WHERE EXCLUDED.updated_at > mytable.updated_at本意是“仅当新时间更新才覆盖”,但如果updated_at是NULL,比较结果为UNKNOWN,更新被跳过,RETURNING也不出数据 - 安全写法:用
WHERE EXCLUDED.updated_at IS NOT NULL AND (mytable.updated_at IS NULL OR EXCLUDED.updated_at > mytable.updated_at) -
RETURNING看不到WHERE是否命中,只能靠返回结果有无来反推
MySQL 或 SQLite 用户看到这别试——语法不兼容
PostgreSQL 的 ON CONFLICT 是专属语法,MySQL 用 ON DUPLICATE KEY UPDATE,SQLite 用 ON CONFLICT REPLACE 或 ON CONFLICT IGNORE,都不支持标准 RETURNING。
兼容性影响:跨数据库抽象 upsert 逻辑时,RETURNING 是最易破防的一环。哪怕 ORM 做了封装(如 SQLAlchemy 的 insert().on_conflict_do_update()),底层仍是方言特有。
- MySQL 的
ON DUPLICATE KEY UPDATE可用LAST_INSERT_ID()获取主键,但无法返回整行 - SQLite 的
RETURNING直到 3.35.0 才实验性支持,且仅限INSERT,不支持ON CONFLICT后跟 - 如果业务强依赖 upsert 后立刻读新状态,优先锁定 PostgreSQL,别指望 ORM 自动兜底
最常被忽略的是:冲突检测依据的是索引(unique constraint / exclusion constraint),不是你写的列名;而 RETURNING 返回的是语句执行后表中的最终状态——它可能来自插入,也可能来自更新,还可能什么都没返回。得看清楚执行路径,而不是只盯着 SQL 表面。










