首页/数据/SQL 速查/MERGE 语句(PostgreSQL 15+)
变更与事务52 条

MERGE 语句(PostgreSQL 15+)

标准化的“合并”语句:按条件匹配则更新、不匹配则插入(还可 WHEN NOT MATCHED BY SOURCE 时删除);PostgreSQL 15 起支持,MySQL 无此语句。

SQL 速查数据更新于 2026-10-13

MERGE 语句是什么

MERGE 是 SQL 标准的「匹配则更新、不匹配则插入」语句:MERGE INTO 目标 USING 来源 ON 匹配条件,后面跟若干 WHEN MATCHED THEN UPDATE、WHEN NOT MATCHED THEN INSERT 分支,可以按不同条件走不同动作。PostgreSQL 15 才引入它,Oracle 与 SQL Server 早就有;MySQL 到 8.0 仍没有 MERGE,等价能力靠 INSERT … ON DUPLICATE KEY UPDATE 实现。MERGE 便利但并发不设防:并发会话可能同时判定不匹配,唯一约束才是最终保障。

基本信息

适用平台PostgreSQL 15+、Oracle、SQL Server;MySQL 不支持
分支类型WHEN MATCHED(可带 AND 条件)、WHEN NOT MATCHED
PostgreSQL 限制15 / 16 只有按目标表匹配的两类分支,按来源删除多余目标行需另外写 DELETE
MySQL 等价INSERT … ON DUPLICATE KEY UPDATE(依赖主键或唯一键)
并发风险MERGE 不是并发去重手段,重复行的防线是唯一约束

典型使用场景

  • 数据对账合并:把来源表的新增与变更合并进目标表
  • 维表刷新:商品、门店等维度数据的批量更新与补插
  • ETL 分层加载:把暂存层数据合并到正式层
  • 迁移脚本:把旧库数据按业务键并入新库
  • 按条件分支更新:只在值确实变化时才写库,减少无效更新

PostgreSQL 15+ 写法

PostgreSQL 15+ 写法
MERGE INTO products AS t
USING staging_products AS s
   ON t.sku = s.sku                       # 匹配键,目标表最好有唯一约束
WHEN MATCHED AND s.price IS DISTINCT FROM t.price THEN
     UPDATE SET price = s.price, updated_at = now()   # 仅当值变化时更新
WHEN MATCHED THEN
     UPDATE SET check_count = t.check_count + 1       # 其他匹配行只累加计数
WHEN NOT MATCHED THEN
     INSERT (sku, name, price, updated_at)
     VALUES (s.sku, s.name, s.price, now());

MySQL 等价写法

MySQL 等价写法
# MySQL 没有 MERGE,用 upsert 实现
INSERT INTO products (sku, name, price, updated_at)
VALUES ('A1', 'Product A', 19.9, NOW())
AS new                                    # 8.0.20 起推荐用行别名引用新值
ON DUPLICATE KEY UPDATE
  name = new.name, price = new.price, updated_at = new.updated_at;

-- 批量合并:逐行 VALUES 形式可用行别名;INSERT … SELECT 形式则用 VALUES(col) 旧写法
INSERT INTO products (sku, name, price, updated_at)
SELECT sku, name, price, NOW() FROM staging_products
ON DUPLICATE KEY UPDATE
  name = VALUES(name), price = VALUES(price), updated_at = NOW();

MERGE 与 upsert 对照

能力PostgreSQL MERGEMySQL upsert
基本语句MERGE INTO … USING … ON …INSERT … ON DUPLICATE KEY UPDATE
多条件分支可写多个 WHEN 分支只有一套统一的更新动作
只更新有变化的行分支里加 AND 条件即可需在 UPDATE 表达式里自行判断
删除来源缺少的目标行另写 DELETE另写 DELETE
依赖匹配条件在 ON 里给定必须有主键或唯一键才会触发更新
MySQL 的 upsert 里引用新值,8.0.20 起推荐行别名写法(INSERT … VALUES (…) AS new ON DUPLICATE KEY UPDATE c = new.c),旧的 VALUES(col) 形式已被标记为不推荐。
MERGE 与 upsert 都不是并发安全的:两个会话可能同时判定「不匹配」并各自插入。唯一约束或主键是最后一道防线,缺少约束时会静默写进重复行。

易错点

  • 以为 MERGE 能替代唯一约束做并发去重,并发下依然写出重复行
  • ON 条件用了非唯一列,一条来源行匹配到多条目标行,更新结果不确定
  • 在 MySQL 上写 MERGE 语法,语句不存在直接报错,需改用 upsert 实现
  • 把 MERGE 当作全量同步:来源里已删除的行不会从目标删除,必须另外写 DELETE

数据来源与参考

  • PostgreSQL Documentation · MERGE
  • MySQL 8.0 Reference Manual · INSERT ... ON DUPLICATE KEY UPDATE
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌