PostgreSQL正确写法为UPDATE t SET col = src.val FROM (SELECT ...) AS src WHERE t.id = src.t_id,目标表不可出现在FROM中;SQL Server为UPDATE t SET t.col = s.val FROM table1 t INNER JOIN table2 s ON t.id = s.t_id,目标表必须在FROM中显式出现并用别名。

UPDATE ... FROM 语法在 PostgreSQL 和 SQL Server 中怎么写
PostgreSQL 和 SQL Server 允许直接用 UPDATE ... FROM 关联子查询或表,把查询结果当“值源”更新目标表。这不是标准 SQL,但很实用。
常见错误是照着 MySQL 写法硬套,比如加 JOIN 或漏掉别名——这两者都会报错。
- PostgreSQL 要求目标表必须有别名(哪怕只写
UPDATE t SET ... FROM ...),且FROM后的表/子查询不能和目标表同名(否则歧义) - SQL Server 不强制目标表别名,但推荐加;
FROM子句里可以显式JOIN,语义更清晰 - 两者都不支持在
SET右侧直接写无关联的子查询(如SET col = (SELECT x FROM other)),除非该子查询返回单行单列且与外层无关——这属于标量子查询,不是“结果集作为值源”
-- PostgreSQL 示例:用 orders 表更新 users 的 last_order_date UPDATE users u SET last_order_date = o.order_date FROM orders o WHERE u.id = o.user_id AND o.created_at = ( SELECT MAX(created_at) FROM orders o2 WHERE o2.user_id = u.id );
MySQL 没有 UPDATE ... FROM,得用 JOIN 或子查询
MySQL 不支持 UPDATE ... FROM,强行写会报错 ERROR 1064。正确做法是用 UPDATE ... JOIN,或者嵌套相关子查询(但性能差)。
容易踩的坑是 JOIN 时没加 ON 条件,或误把子查询写成不相关形式导致全表更新——比如漏掉 WHERE user_id = users.id,结果所有用户的字段被设成同一个值。
-
UPDATE ... JOIN是最常用、性能最好的方式,等价于FROM语义 - 标量子查询(
SET col = (SELECT ...))必须确保每行只返回一行,否则报错Subquery returns more than 1 row - 不能在同一个语句中从被更新的表里直接查(除非用派生表绕过),否则报错
You can't specify target table 'xxx' for update in FROM clause
-- MySQL 正确写法:用 JOIN 关联查询结果 UPDATE users u JOIN ( SELECT user_id, MAX(order_date) AS max_date FROM orders GROUP BY user_id ) o ON u.id = o.user_id SET u.last_order_date = o.max_date;
Oracle 和 SQLite 怎么办:只能靠相关子查询
Oracle 和 SQLite 不支持 FROM 或 JOIN 在 UPDATE 中,唯一可行的是在 SET 右侧写相关子查询(correlated subquery)。
关键限制是子查询必须能按主表每行独立执行,且返回至多一行一列。稍不注意就触发 ORA-01427: single-row subquery returns more than one row 或空值问题。
- 必须在子查询
WHERE中显式关联主表字段,比如WHERE o.user_id = users.id - 建议用
ROWNUM = 1(Oracle)或LIMIT 1(SQLite)兜底防多行,但要意识到这会丢数据 - 如果子查询可能为空,
SET字段会被设为NULL,需用NVL或COALESCE处理
-- Oracle 示例:用相关子查询取最新订单日期 UPDATE users u SET last_order_date = ( SELECT MAX(o.order_date) FROM orders o WHERE o.user_id = u.id );
跨数据库移植时最容易忽略的兼容性点
不同数据库对“查询结果集作值源”的实现差异极大,同一逻辑换库就得重写,而且错误提示五花八门,很难一眼定位问题根源。
最常被忽略的是别名规则和空值行为:PostgreSQL 的 FROM 允许 LEFT JOIN 产生 NULL,MySQL 的 JOIN 默认 INNER,Oracle 的相关子查询空结果直接设 NULL——这些细微差别会导致线上数据被意外清空或覆盖。
真正麻烦的不是语法本身,而是你得先判断当前数据库版本是否支持某种写法(比如旧版 PostgreSQL 8.3 不支持 UPDATE ... FROM),再决定要不要加中间临时表或拆成两步。

















