














在数据库数据同步场景中,“匹配则更新、不匹配则插入”(即 “UPSERT”)是高频需求。Oracle 原生支持 MERGE 语句,SQL Server(MSSQL)后续跟进了类似语法,而 MySQL 则通过专属语法实现等效功能。本文将详细对比三大主流数据库的实现方案、语法细节及使用注意事项,附可直接运行的实战示例。
Oracle 是最早原生支持 MERGE INTO 语句的数据库之一,语法简洁直观,无需依赖额外约束,仅通过 ON 子句定义匹配条件即可。
MERGE INTO 目标表 别名 USING 源表/子查询 别名 ON (匹配条件) WHEN MATCHED THEN -- 匹配成功:执行更新 UPDATE SET 字段=值 WHEN NOT MATCHED THEN -- 匹配失败:执行插入 INSERT (字段列表) VALUES (值列表);
-- 同步待同步标识为1、指定租户的组织数据到目标表 MERGE INTO sec_org o USING ( SELECT org_id, parent_org_id, org_short_name, org_name, org_type, org_wbs_code, org_wbs_level, orderby, status, remark, update_time, tenant_code FROM sync_org WHERE is_sync = 1 AND tenant_code = 'TENANT001' -- 替换为实际租户编码 ) so ON (o.org_id = so.org_id) -- 以组织ID作为唯一匹配标识 WHEN MATCHED THEN UPDATE SET o.parent_org_id = so.parent_org_id, o.org_name = NVL(so.org_short_name, so.org_name), -- 优先用简称,无则用全名 o.org_type = so.org_type, o.org_wbs_code = so.org_wbs_code, o.org_wbs_level = so.org_wbs_level, o.orderby = so.orderby, o.status = so.status, o.update_time = SYSDATE -- 用当前时间更新,确保时效性 WHEN NOT MATCHED THEN INSERT ( org_id, parent_org_id, org_name, org_type, org_wbs_code, org_wbs_level, orderby, status, remark, create_time, update_time, tenant_code ) VALUES ( so.org_id, so.parent_org_id, NVL(so.org_short_name, so.org_name), so.org_type, so.org_wbs_code, so.org_wbs_level, so.orderby, so.status, so.remark, SYSDATE, SYSDATE, so.tenant_code ); COMMIT; -- Oracle 11.2 需手动提交(非事务环境下)
NVL(字段, 默认值) 函数,当源字段为空时取默认值,等价于其他数据库的 ISNULL/IFNULL;MERGE 语句为单原子操作,更新和插入逻辑要么同时成功,要么同时回滚,避免数据不一致;MERGE 功能,11.2+ 无语法兼容问题;org_id 和源表 org_id 建立索引,减少匹配时的全表扫描。SQL Server 2008 引入 MERGE 语句,语法与 Oracle 高度相似,同时增加了 OUTPUT 子句等特有功能,灵活性更强。
MERGE [ INTO ] 目标表 [ 目标别名 ] USING 源表/子查询 [ 源别名 ] ON (匹配条件) [ WHEN MATCHED [ AND 额外条件 ] THEN UPDATE SET 字段=值 ] [ WHEN NOT MATCHED [ AND 额外条件 ] THEN INSERT (字段列表) VALUES (值列表) ] [ OUTPUT 输出字段 ]; -- MSSQL 独有,可返回操作结果
-- 声明租户编码参数(支持NULL取消过滤) DECLARE @tenantCode VARCHAR(50) = 'TENANT001'; MERGE INTO sec_org o USING ( SELECT org_id, parent_org_id, org_short_name, org_name, org_type, org_wbs_code, org_wbs_level, orderby, status, remark, update_time, tenant_code FROM sync_org WHERE is_sync = 1 AND (@tenantCode IS NULL OR tenant_code = @tenantCode) -- 动态租户过滤 ) so ON (o.org_id = so.org_id) -- 唯一键匹配 WHEN MATCHED THEN UPDATE SET o.parent_org_id = so.parent_org_id, o.org_name = ISNULL(so.org_short_name, so.org_name), -- 替换Oracle的NVL函数 o.org_type = so.org_type, o.org_wbs_code = so.org_wbs_code, o.org_wbs_level = so.org_wbs_level, o.orderby = so.orderby, o.status = so.status, o.update_time = GETDATE() -- MSSQL获取当前时间 WHEN NOT MATCHED THEN INSERT ( org_id, parent_org_id, org_name, org_type, org_wbs_code, org_wbs_level, orderby, status, remark, create_time, update_time, tenant_code ) VALUES ( so.org_id, so.parent_org_id, ISNULL(so.org_short_name, so.org_name), so.org_type, so.org_wbs_code, so.org_wbs_level, so.orderby, so.status, so.remark, GETDATE(), GETDATE(), so.tenant_code ) -- 输出操作结果(INSERT/UPDATE),便于业务校验 OUTPUT inserted.org_id, $action INTO #temp_result; -- 查看同步结果($action值为'INSERT'或'UPDATE') SELECT * FROM #temp_result; DROP TABLE #temp_result; -- 清理临时表 COMMIT;
NVL 对应 MSSQL 的 ISNULL,功能完全一致;也支持跨库通用的 COALESCE 函数;@变量 接收参数,结合 OR 变量 IS NULL 实现 “可选过滤”,无需框架拼接 SQL;OUTPUT 子句可返回插入 / 更新的记录及操作类型,方便后续业务处理(如日志记录、告警触发);MERGE 功能,2005 及以下版本需拆分为 UPDATE + INSERT 语句;WITH (NOLOCK) 优化(需评估业务风险)。MySQL 无原生 MERGE 关键字,提供两种等效方案,其中 INSERT ... ON DUPLICATE KEY UPDATE 最贴合 “匹配更新、不匹配插入” 语义,是推荐方案。
INSERT INTO 目标表 (字段列表) SELECT 源字段列表 FROM 源表 WHERE 过滤条件 ON DUPLICATE KEY UPDATE 字段1=值1, 字段2=值2, ...;
目标表 sec_org 的 org_id 字段必须是主键或唯一索引,否则 ON DUPLICATE KEY 不生效,会直接报主键冲突错误。
-- 同步组织数据,匹配则更新,不匹配则插入 INSERT INTO sec_org ( org_id, parent_org_id, org_name, org_type, org_wbs_code, org_wbs_level, orderby, status, remark, create_time, update_time, tenant_code ) SELECT org_id, parent_org_id, IFNULL(org_short_name, org_name), -- 替换Oracle的NVL org_type, org_wbs_code, org_wbs_level, orderby, status, remark, NOW(), NOW(), tenant_code -- MySQL获取当前时间 FROM sync_org WHERE is_sync = 1 AND (tenant_code = #{tenantCode} OR #{tenantCode} IS NULL) -- 动态条件(MyBatis适配) ON DUPLICATE KEY UPDATE -- 匹配org_id唯一键时执行更新 parent_org_id = VALUES(parent_org_id), -- VALUES(字段)引用插入的源值 org_name = VALUES(org_name), org_type = VALUES(org_type), org_wbs_code = VALUES(org_wbs_code), org_wbs_level = VALUES(org_wbs_level), orderby = VALUES(orderby), status = VALUES(status), update_time = NOW(); -- 更新时间刷新为当前时间 COMMIT; -- InnoDB引擎需手动提交
REPLACE INTO sec_org ( org_id, parent_org_id, org_name, org_type, org_wbs_code, org_wbs_level, orderby, status, remark, create_time, update_time, tenant_code ) SELECT org_id, parent_org_id, IFNULL(org_short_name, org_name), org_type, org_wbs_code, org_wbs_level, orderby, status, remark, NOW(), NOW(), tenant_code FROM sync_org WHERE is_sync = 1 AND (tenant_code = #{tenantCode} OR #{tenantCode} IS NULL);
creator 创建人),旧行的该字段值会被置空;IFNULL(字段, 默认值) 或跨库通用的 COALESCE 函数;org_id 为主键 / 唯一索引,需提前确认表结构;INSERT ... ON DUPLICATE KEY UPDATE 从 MySQL 4.1 开始支持,无版本兼容风险;LIMIT 限制同步行数(如 LIMIT 1000),避免一次性操作过多数据导致锁表。
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。