MERGE INTO开发教程

MERGE INTO 是 SQL 里的"一条语句搞定增删改"操作,也叫 UPSERT(存在则更新,不存在则插入),常用于数据同步、ETL 增量入库。

一、标准语法骨架

复制代码
MERGE INTO 目标表 [AS] t
USING 数据源 [AS] s
  -- 数据源可以是表、视图、子查询、VALUES
ON (t.匹配键 = s.匹配键)
WHEN MATCHED THEN
  UPDATE SET t.col1 = s.col1, t.col2 = s.col2
  -- 可选 WHERE / AND 条件,Oracle 还可加 DELETE WHERE
WHEN NOT MATCHED THEN
  INSERT (col1, col2) VALUES (s.col1, s.col2);
  • MERGE INTO:被修改的表(target)

  • USING:数据来源(source),可以是表也可以是 (SELECT ...)(VALUES ...)

  • ON只写匹配条件 (如主键相等),不要把常量过滤塞进去,过滤放 WHEN ... AND

  • WHEN MATCHED:目标有、源也有 → 更新或删除

  • WHEN NOT MATCHED:源有、目标没有 → 插入

  • 至少要有 1 个 WHEN 分支;SQL Server 必须以分号 ; 结尾

二、最常用:UPSERT(更新或插入)

复制代码
MERGE INTO employees t
USING new_employees s
ON (t.emp_id = s.emp_id)
WHEN MATCHED THEN
  UPDATE SET t.name = s.name,
             t.salary = s.salary,
             t.update_time = CURRENT_TIMESTAMP
WHEN NOT MATCHED THEN
  INSERT (emp_id, name, salary, create_time)
  VALUES (s.emp_id, s.name, s.salary, CURRENT_TIMESTAMP);

效果:工号存在就刷新姓名薪资,不存在就新增。等价于 MySQL 的 INSERT ... ON DUPLICATE KEY UPDATE

三、带条件更新(避免无变化也写 redo)

Oracle / PostgreSQL / SQL Server 都支持在分支上加 AND

复制代码
WHEN MATCHED AND s.salary <> t.salary THEN
  UPDATE SET t.salary = s.salary

Oracle 还允许在 UPDATE 后直接跟 WHERE:

复制代码
WHEN MATCHED THEN
  UPDATE SET t.price = s.price
  WHERE t.price <> s.price
  DELETE WHERE s.discontinued = 'Y';   -- 匹配上且满足条件则删

注意:Oracle 里 ON 条件用到的列不能在 UPDATE SET 里改,否则报 ORA-38104。

四、源是子查询 / VALUES(不用建临时表)

PostgreSQL 单行 upsert:

复制代码
MERGE INTO users t
USING (VALUES ('u1001', 'Alice', 28)) AS s(id, name, age)
ON t.id = s.id
WHEN MATCHED THEN UPDATE SET name = s.name, age = s.age
WHEN NOT MATCHED THEN INSERT (id, name, age) VALUES (s.id, s.name, s.age);

五、删除"目标有、源没有"的脏数据

SQL Server / PostgreSQL 17+ / Delta Lake 支持 WHEN NOT MATCHED BY SOURCE

复制代码
MERGE INTO employees t
USING new_employees s
ON t.emp_id = s.emp_id
WHEN MATCHED THEN
  UPDATE SET t.salary = s.salary
WHEN NOT MATCHED THEN
  INSERT (emp_id, name, salary) VALUES (s.emp_id, s.name, s.salary)
WHEN NOT MATCHED BY SOURCE THEN
  DELETE;   -- 源里没这条工号,目标里删掉

六、各数据库支持情况

数据库 支持度 备注
Oracle 9i+ 最老牌,DELETE WHERE 是扩展
SQL Server 2008+ 语句结尾必须 ;;历史版本有已知 bug,高并发建议评估
PostgreSQL 15+ 17+ 才支持 WHEN NOT MATCHED BY SOURCE
MySQL INSERT ... ON DUPLICATE KEY UPDATEREPLACE
SQLite 3.24+ 语法接近标准
Spark / Delta / MaxCompute 数据仓库场景常用

七、常见踩坑

  1. ON 里只写"怎么算同一条",别写业务过滤 (如 AND status='A' 应挪到 WHEN MATCHED AND status='A'),否则匹配集错乱。

  2. 源数据有重复键 ​ → 同一目标行被匹配两次,SQL Server / Oracle 直接报错;MERGE 前先 ROW_NUMBER() 去重。

  3. ON 的键必须有唯一约束(主键/唯一索引),否则"是否存在"语义不成立。

  4. SQL Server 的 MERGE 在触发器、@@ROWCOUNT、并发下有历史坑,超大数据量同步有时分开写 UPDATE + INSERT 更稳。

  5. 一条 MERGE 里同一行不能被又 UPDATE 又 DELETE,分支条件要互斥。


如果你告诉我用的是 Oracle / SQL Server / PostgreSQL / MySQL​ 哪一种,以及是"两表同步"还是"单条记录 upsert",我可以直接给你一段能跑的版本。

相关推荐
姜太小白5 小时前
【Oracle】记排查sh脚本调用SQL执行卡顿的排查总结
数据库·sql·oracle
镜舟科技7 小时前
为什么 Text-to-SQL 总是停在 Demo?
数据库·sql·demo·text-to-sql·镜舟科技·mip·语义视图
大牧师8 小时前
TypeORM 学习教程
数据库·sql·学习·node.js·orm·nest.js·typeorm
数智启示录10 小时前
PostgreSQL 执行计划实战(第 9 篇):SQL 和索引没变,计划为什么突然慢一百倍
数据库·经验分享·sql·postgresql·面试
润乾软件11 小时前
SQLazy:从事件表中查出下一组的开始时刻
sql·sqlazy
养生技术人12 小时前
Oracle OCP认证考试题目详解082系列第17题
运维·数据库·sql·oracle·ocp
隔窗听雨眠13 小时前
活动中台系统慢SQL治理实践:从监控告警到性能优化的全链路方法论
sql·性能优化·github
数智启示录1 天前
PostgreSQL 计划缓存实战(第 10 篇):预编译 SQL 前五次都快,第六次为什么可能变慢
经验分享·sql·缓存·postgresql·面试
ClouGence1 天前
CloudDM 支持达梦、KingbaseES、GoldenDB,国产数据库也能统一管起来
数据库·sql·开源
CarIise1 天前
JDBC数据库操作与SQL注入防范
数据库·sql