JDBI3搭配Oracle数据库实现不存在插入存在更新的方案咨询
Merge语句编写注意要点
- 匹配条件字段必须加唯一约束:你当前语句的匹配条件是
(col1, col2),必须在这两个字段上创建联合主键或者联合唯一索引,一是避免Merge执行时全表扫描,二是防止出现ORA-30926: 无法在源表中获得一组稳定的行报错,同时保证数据唯一性符合业务预期。 - 更新字段不能包含匹配条件字段:WHEN MATCHED分支的SET语句里,不能修改col1、col2这两个用于匹配的字段,否则Oracle会直接抛出语法错误。如果你的需求是更新3个非匹配字段,直接在SET后追加对应字段的赋值逻辑即可,示例中你只更新了col4,调整为多字段更新的写法为
SET db.col3 = input.col3, db.col4 = input.col4, db.其他字段 = input.其他字段即可。 - Merge为原子操作:整个语句执行要么全成功要么全回滚,不需要额外在应用层加锁判断,天然避免了并发场景下重复插入的问题。
JDBI3 @SQLUpdate 适配写法
你当前的语句是硬编码的参数,替换为JDBI参数绑定的写法示例如下:
@SQLUpdate("MERGE INTO device db " + "USING (SELECT :col1 AS col1, :col2 AS col2, :col3 AS col3, :col4 AS col4 FROM DUAL) input " + "ON (db.col1 = input.col1 AND db.col2 = input.col2) " + "WHEN MATCHED THEN UPDATE SET db.col3 = input.col3, db.col4 = input.col4 " + "WHEN NOT MATCHED THEN INSERT (col1, col2, col3, col4) " + "VALUES (input.col1, input.col2, input.col3, input.col4)") int upsertDevice(@Bind("col1") String col1, @Bind("col2") String col2, @Bind("col3") String col3, @Bind("col4") String col4);
注意:INSERT后的字段列表不需要加表别名,加别名会触发Oracle语法错误。
高写负载场景下Merge的性能说明
性能影响
- 有正确索引的前提下,单条Merge的性能比「先SELECT判断是否存在、再执行INSERT/UPDATE」的逻辑高30%以上,仅需要一次数据库交互,减少了网络往返开销,也避免了两次SQL执行之间的并发锁冲突。
- 如果没有对应联合索引,Merge会触发全表扫描,表数据量越大性能衰减越严重,高并发场景下会产生大量行锁甚至表锁,阻塞其他写请求。
- 单条循环Merge的性能远低于批量Merge,相同数据量下批量Merge的吞吐量是单条循环的3-10倍。
优化建议
- 优先确保匹配条件的联合唯一索引已创建,这是Merge性能的核心前提。
- 业务允许的情况下尽量使用批量Merge,通过JDBI的
@BindBeanList绑定批量参数,USING子句用UNION ALL拼接多条待写入数据,减少交互次数。 - 避免更新不必要的字段,减少Oracle redo日志的生成量,降低IO压力。
内容的提问来源于stack exchange,提问作者Zanndorin
相关产品推荐
相关产品推荐

