从 MySQL 迁过来的同事写了条 UPDATE a JOIN b ON a.id = b.id SET a.name = b.name,Oracle 直接抛 ORA-00933: SQL command not properly ended。他以为是版本问题,换了个客户端还是一样。
语法本身不存在
Oracle 的 UPDATE 只能针对一张表或一个可更新视图,JOIN 不能出现在 UPDATE 后面。这不是版本差异,是方言差异。硬套 MySQL 写法,改到 Oracle 23ai 也过不了。
用 MERGE 做批量更新
MERGE INTO users u USING tmp_users t ON (u.id = t.id) WHEN MATCHED THEN UPDATE SET u.name = t.name;USING 后面可以是表、子查询或视图。ON 条件要能唯一确定一行,重复会报 ORA-30926。只想更新部分列就写在 SET 里,别整行覆盖。MERGE 也能做插入,WHEN NOT MATCHED THEN INSERT,适合做同步。
子查询写法
UPDATE users u SET u.name = (SELECT t.name FROM tmp_users t WHERE t.id = u.id) WHERE EXISTS (SELECT 1 FROM tmp_users t WHERE t.id = u.id)。少了 EXISTS,没匹配的行会被置空。子查询返回多行则报 ORA-01427,先确认关联键唯一。
性能和注意事项
MERGE 对大表可能做全表扫描,关联列要有索引,执行计划里看有没有 MERGE JOIN CARTESIAN。更新完记得 COMMIT,Oracle 不会自动提交。生产库先在小库跑一遍,统计影响行数,再决定要不要分批。触发器和物化视图也要检查,批量更新可能把它们一起点火。
迁移项目里,先把 UPDATE JOIN 全搜出来改掉,比运行时报错再找快得多。