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 MERGE | MySQL 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 都不是并发安全的:两个会话可能同时判定「不匹配」并各自插入。唯一约束或主键是最后一道防线,缺少约束时会静默写进重复行。