首页/数据/SQL 速查/跨表更新:UPDATE ... FROM 与 UPDATE JOIN
变更与事务52 条

跨表更新:UPDATE ... FROM 与 UPDATE JOIN

用另一张表的数据更新本表:PostgreSQL 用 UPDATE ... FROM,MySQL 用 UPDATE ... JOIN;注意一对多匹配的不确定性。

SQL 速查数据更新于 2026-10-13

跨表更新是什么

用另一张表的数据更新本表:PostgreSQL 写 UPDATE ... FROM,MySQL 写 UPDATE ... JOIN,语义都是按关联条件把源表的值刷到目标表。风险在于源表对同一目标行有多条匹配时,用哪一条由执行计划决定,结果不确定。

基本信息

PostgreSQLUPDATE 目标表 SET … FROM 源表 WHERE 关联条件
MySQLUPDATE 目标表 JOIN 源表 ON … SET …
一对多风险源表同一键有多行时取值不确定
MySQL 限制多表 UPDATE 不支持 ORDER BY 与 LIMIT
PG 注意别把目标表重复写进 FROM,会变成自连接
自检方式先把源表按键聚合成一行,或确认源表键唯一

PostgreSQL 写法

PostgreSQL 写法
UPDATE orders o
SET status  = p.status,
    paid_at = p.paid_at
FROM payment p
WHERE p.order_id = o.id
  AND p.status = 'success';   -- 只更新有成功支付记录的订单
-- 注意:FROM 里不要再写 orders,否则会变成自连接导致结果放大

MySQL 写法

MySQL 写法
UPDATE orders o
JOIN payment p ON p.order_id = o.id AND p.status = 'success'
SET o.status  = p.status,
    o.paid_at = p.paid_at;
-- 多表 UPDATE 不支持 ORDER BY 与 LIMIT
-- 想分批更新请改用带主键范围条件的 WHERE

两库写法对照

需求PostgreSQLMySQL
单源表更新UPDATE … FROM 源表 WHERE 关联UPDATE … JOIN 源表 ON 关联
只更新匹配行FROM 加 WHERE 相当于内连接语义JOIN 默认就是内连接语义
保留未匹配行未匹配行保持原值不变同样只更新匹配到的行
左连接更新在 FROM 中加条件或用子查询UPDATE … LEFT JOIN … ON …
批量限制可用 CTE 与子查询控制范围多表 UPDATE 不支持 ORDER BY / LIMIT

典型使用场景

  • 状态回填:用支付表刷新订单的支付状态与时间
  • 维表补齐:把商品名称、分类刷进明细表
  • 数据修正:按对照表批量修正错误的编码
  • 分层汇总:把汇总表结果刷回主表,避免每次实时计算
  • 迁移过渡期双写校验:用一张表的结果校正另一张表
  • 批量改价:用价格表刷新商品表的最新售价
源表一对多时先聚合再更新:把源表按关联键聚合成一行(取最大、最新一条或合计),再参与更新,结果才确定。否则同一条 SQL 两次执行可能给出不同结果。
更新前先跑一遍 SELECT 校验影响范围:把同样的关联条件写成 SELECT COUNT(*),确认待更新行数与预期一致,并确认源表关联键唯一,然后再执行 UPDATE。

易错点

  • 源表一对多直接更新:取值由执行计划决定,两次执行结果可能不同,先聚合源表再更新
  • PG 里把目标表重复写进 FROM:形成自连接后结果成倍放大,UPDATE 目标表只能出现一次
  • MySQL 多表 UPDATE 想用 LIMIT 控制批量:语法不支持,改用主键范围条件分批
  • 漏写关联条件:退化成笛卡尔积更新,可能把整表刷成同一个值,执行前先确认命中范围

数据来源与参考

  • PostgreSQL Documentation · UPDATE
  • MySQL 8.0 Reference Manual · UPDATE
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌