postgresql 16中update from要求目标表仅在update后声明,不得出现在from中;必须含t.id = src.id类显式关联条件,否则触发笛卡尔积导致误更新;from可接子查询(须带别名),推荐先用等价select验证匹配行数与唯一性。

PostgreSQL 16 的 UPDATE FROM 不支持目标表出现在 FROM 中
你写的 UPDATE a FROM a JOIN b 会静默失效,不是语法报错,而是 JOIN 部分被完全忽略——因为目标表 a 在 UPDATE 后已声明,又在 FROM 中重复出现,触发自引用歧义。PostgreSQL 16 沿用旧规则:目标表只能在 UPDATE 关键字后出现一次,FROM 子句里必须是“其他表”或封装好的子查询。
必须显式写出关联条件,否则极易全表误更新
漏掉 WHERE t.id = src.t_id 这类显式等值连接,会导致 FROM 子句产生笛卡尔积,UPDATE 就会匹配到所有组合行,最终把目标表每一行都更新成源数据的某一行(通常是第一行或最后一行,取决于执行计划),且返回“UPDATE 1000”这种看似成功实则错误的结果。
- 永远不要只靠
src.status = 'active'这类过滤条件,它不建立表间关系 - WHERE 中至少要有一个
t.xxx = src.yyy形式的等值关联 - 多对一场景(如一个用户多条订单)下,建议加
DISTINCT ON (t.id)或聚合,避免重复匹配
FROM 后只能跟一个“源结构”,但可以是嵌套 JOIN
FROM 后不能平铺写 b, c 或 b JOIN c JOIN d,但可以把它包进一层子查询或带别名的 JOIN 表达式里:
PostgreSQL 18.4 官方 Ubuntu 安装包现已发布,这是目前最新的稳定版本。推荐通过官方 APT 仓库安装:先执行 sudo apt update 更新索引,再运行 sudo apt install postgresql-18 即可完成部署。新版本引入了异步 I/O 子系统,在顺序扫描与 VACUUM 场景下性能提升显著,同时支持 UUID v7 原生生成函数与虚拟生成列。
UPDATE users u SET email = src.email, updated_at = NOW() FROM ( SELECT user_id, email FROM new_contacts WHERE is_valid ) AS src WHERE u.id = src.user_id;
- 子查询必须有别名(如
AS src),否则报错missing FROM-clause entry - 子查询里字段名若与目标表冲突(比如都有
id),必须用别名限定,否则解析失败 - 嵌套太深会影响可读性,建议复杂逻辑先用
WITH提前定义,再在FROM中引用
验证更新范围最可靠的方法:把 FROM + WHERE 拆出来跑 SELECT
写完 UPDATE ... FROM ... WHERE ... 后,把 FROM 和 WHERE 部分单独拿出来,拼成等价 SELECT:
SELECT u.id, src.email FROM users u JOIN new_contacts src ON u.id = src.user_id WHERE src.is_valid;
- 这个
SELECT返回的行数,就是你即将更新的行数 - 检查结果里是否有重复
u.id—— 如果有,说明存在一对多匹配,更新结果不可控 - 如果
SELECT返回 0 行,但你预期有数据,问题一定出在关联条件或数据本身(比如 NULL 值导致等值不成立)
真正容易被忽略的是:PostgreSQL 16 对 JSONB 字段、分区表和 GENERATED 列的更新行为没有变化,但如果你在 FROM 子查询里用了这些特性,执行计划可能意外走 Seq Scan,务必用 EXPLAIN 看实际路径。










