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 UPDATE 或 REPLACE |
| SQLite 3.24+ | ✅ | 语法接近标准 |
| Spark / Delta / MaxCompute | ✅ | 数据仓库场景常用 |
七、常见踩坑
-
ON 里只写"怎么算同一条",别写业务过滤 (如
AND status='A'应挪到WHEN MATCHED AND status='A'),否则匹配集错乱。 -
源数据有重复键 → 同一目标行被匹配两次,SQL Server / Oracle 直接报错;MERGE 前先
ROW_NUMBER()去重。 -
ON 的键必须有唯一约束(主键/唯一索引),否则"是否存在"语义不成立。
-
SQL Server 的 MERGE 在触发器、
@@ROWCOUNT、并发下有历史坑,超大数据量同步有时分开写UPDATE+INSERT更稳。 -
一条 MERGE 里同一行不能被又 UPDATE 又 DELETE,分支条件要互斥。
如果你告诉我用的是 Oracle / SQL Server / PostgreSQL / MySQL 哪一种,以及是"两表同步"还是"单条记录 upsert",我可以直接给你一段能跑的版本。