SQL MERGE标准语法不支持直接删除不匹配行,仅SQL Server和Oracle扩展支持WHEN NOT MATCHED BY SOURCE THEN DELETE,PostgreSQL和MySQL不识别该语法,须用DELETE+INSERT+UPDATE三步替代。

SQL MERGE 语句不能直接 DELETE 不匹配的行
MERGE 本身不支持「当源数据中没有对应记录时就删除目标表中的行」这种逻辑。标准 SQL 的 MERGE 只定义了 WHEN MATCHED(更新或跳过)和 WHEN NOT MATCHED(插入),但没有 WHEN NOT MATCHED BY SOURCE——那是 SQL Server 和 Oracle 的扩展语法,PostgreSQL 和 MySQL 根本不支持。
这意味着:如果你在 PostgreSQL 或 MySQL 中写 MERGE 想顺带删掉旧数据,会直接报错或语法不识别。
常见错误现象:
- PostgreSQL 报错:
ERROR: syntax error at or near "MERGE" - MySQL 8.0+ 虽支持
MERGE,但仅限于 NDB 存储引擎,InnoDB 不支持 - 即使在 SQL Server 中写了
WHEN NOT MATCHED BY SOURCE THEN DELETE,也要求目标表有唯一约束或主键,否则报错The MERGE statement attempted to UPDATE or DELETE the same row more than once
SQL Server 中用 MERGE 实现 INSERT + UPDATE + DELETE 的完整写法
只有 SQL Server(2008+)和 Oracle(10g+)真正支持三合一操作。以 SQL Server 为例,关键在于显式启用 WHEN NOT MATCHED BY SOURCE THEN DELETE,且必须满足匹配列上有唯一索引。
实操建议:
- 目标表
target_table必须在ON子句中使用的列(如id)上有主键或唯一索引 -
USING的源数据不能含重复id,否则触发「同一行被多次更新/删除」错误 -
DELETE子句不能带WHERE条件(SQL Server 不允许),但可通过在USING子查询里预过滤来间接控制 - 整个
MERGE是原子操作,但DELETE部分会锁住整张目标表的匹配范围,高并发下注意阻塞
示例(SQL Server):
MERGE target_table AS t USING (VALUES (1, 'a'), (2, 'b')) AS s(id, name) ON t.id = s.id WHEN MATCHED THEN UPDATE SET name = s.name WHEN NOT MATCHED THEN INSERT (id, name) VALUES (s.id, s.name) WHEN NOT MATCHED BY SOURCE THEN DELETE;
MySQL / PostgreSQL 用户该怎么做?
老老实实用三步:先删、再插、最后更新(或反过来),靠事务保证一致性。别硬套 MERGE,徒增复杂度。
典型替代方案:
- MySQL:用
INSERT ... ON DUPLICATE KEY UPDATE处理存在性逻辑,删除需单独DELETE+LEFT JOIN或临时表 - PostgreSQL:用
INSERT ... ON CONFLICT DO UPDATE,删除同样需额外语句;也可用WITHCTE 配合NOT EXISTS实现单条语句内完成 - 性能影响:三步法比原生
MERGE多一次全表扫描(用于找待删行),但胜在可预测、易调试
PostgreSQL 示例(安全可控):
BEGIN; -- 先删掉 source 里没有的旧记录 DELETE FROM target_table WHERE id NOT IN (SELECT id FROM source_table WHERE id IS NOT NULL); <p>-- 再 upsert INSERT INTO target_table (id, name) SELECT id, name FROM source_table ON CONFLICT (id) DO UPDATE SET name = EXCLUDED.name; COMMIT;
为什么很多人踩坑?关键点容易被忽略
最常被忽视的是匹配条件的完备性:比如 ON t.id = s.id 看似简单,但如果 s.id 里有 NULL,SQL Server 会直接跳过该行(因为 NULL = NULL 为 UNKNOWN),导致本该删除的行没删,本该插入的也没插。
另一个隐形门槛:SQL Server 的 WHEN NOT MATCHED BY SOURCE 删除行为,只作用于本次 MERGE 所涉及的 ON 匹配范围内——它不会删掉其他未参与匹配的行,也不会自动清理历史残留。
所以实际使用时,务必确认源数据覆盖了你希望保留的所有主键值,否则「删多」或「删漏」都可能静默发生。

















