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",我可以直接给你一段能跑的版本。

相关推荐
这个DBA有点耶2 小时前
交易型数据库是什么?OLTP核心能力与2026选型指南
数据库·数据仓库·sql·database·数据库架构·olap·dba
提笔了无痕6 小时前
MySQL SQL 从 EXPLAIN 到索引优化,搞懂 SQL 为什么慢
android·sql·mysql
辰同学ovo8 小时前
从一条 SQL 到三个原则:MySQL 心智模型
sql·mysql·adb
网安墨雨11 小时前
MySQL数据库 SQL语句详解
自动化测试·软件测试·数据库·python·sql·mysql
一个天蝎座 白勺 程序猿14 小时前
复盘之我在金仓生产环境踩过的SQL暗坑,和攒了六年的编码规矩
数据库·sql·kingbasees
AAA@峥1 天前
CentOS7 源码编译安装 MySQL5.7|SQL 基础操作 + 备份恢复完整实战
运维·数据库·sql·centos
IvorySQL2 天前
PG 日报|修复高 IO 并发场景,解决预读机制耗尽本地缓冲区缺陷
数据库·人工智能·sql·postgresql·区块链