• 一条merge实现插入或更新


        Oracle 提供了 merge 语句可以通过一条 SQL 语句实现 insert 和 update 操作,但是如果使用不当,会遇到奇怪的问题。为什么明明没有数据,确没有执行 insert 操作,也没有执行 update 操作,好像 merge 语句不工作了。

    1. 建表语句

    1. CREATE TABLE TEST_TB1 (
    2. ID NUMBER(10,0),
    3. NAME VARCHAR2(100) NOT NULL,
    4. REMARK VARCHAR2(100),
    5. CONSTRAINT PK_TEST_TB1 PRIMARY KEY (ID)
    6. );

    2. merge 语句1

    使用 SQLPlus 连接数据库:

    1. instantclient_11_2/sqlplus.exe test_db/test_db@192.1.1.166:1521/tsdb
    2. # 查询表数据
    3. SQL> select * from TEST_TB1;
    4. no rows selected

    merge语句1: 

    1. MERGE INTO TEST_TB1 a
    2. USING (SELECT zz.id FROM TEST_TB1 zz WHERE zz.id = 1) b
    3. ON (a.id = b.id)
    4. WHEN MATCHED THEN
    5. UPDATE SET NAME = 'Tom', REMARK = 'Tom update'
    6. WHEN NOT MATCHED THEN
    7. INSERT (a.id, a.name, a.remark)
    8. values (1, 'Tom', 'Tom insert');

    执行结果:

    0 rows merged.

    很奇怪为什么没有执行 insert 操作。

    3. 修复后的 merge 语句2

    1. MERGE INTO TEST_TB1 a
    2. USING ( SELECT ( SELECT zz.ROWID AS rid FROM TEST_TB1 zz WHERE zz.id = 1) AS rid FROM dual) b
    3. ON (a.ROWID = b.rid)
    4. WHEN MATCHED THEN
    5. UPDATE SET NAME = 'Tom', REMARK = 'Tom update'
    6. WHEN NOT MATCHED THEN
    7. INSERT (a.id, a.name, a.remark)
    8. values (1, 'Tom', 'Tom insert');

    执行结果:

    1 row merged. 

    看看数据吧

    1. SQL> select * from TEST_TB1;
    2. ID
    3. ----------
    4. NAME
    5. --------------------------------------------------------------------------------
    6. REMARK
    7. --------------------------------------------------------------------------------
    8. 1
    9. Tom
    10. Tom update

    执行成功了!

    4. 两个 merge 语句 source table 对比 

    merge 语句1 中 source table 的查询结果:

    1. SQL> SELECT zz.id FROM TEST_TB1 zz WHERE zz.id = 2;
    2. no rows selected

     merge 语句2中 source table 的查询结果:

    1. SQL> SELECT ( SELECT zz.ROWID AS rid FROM TEST_TB1 zz WHERE zz.id = 2) AS rid FROM dual;
    2. RID
    3. ------------------

    5. 并发问题

    虽然解决了 merge 语句问题,还是要注意 merge 语句并发问题。

    使用 contiperf 进行并发测试:

    1. <dependency>
    2. <groupId>org.databenegroupId>
    3. <artifactId>contiperfartifactId>
    4. <version>2.3.4version>
    5. <scope>testscope>
    6. dependency>

    merge 语句配置

    1. id="insertByMerge" parameterType="com.ybst.note.db.comm.entity.TestTb1">
    2. MERGE INTO TEST_TB1 a
    3. USING ( SELECT ( SELECT zz.ROWID AS rid FROM TEST_TB1 zz WHERE zz.id = #{id}) AS rid
    4. FROM dual) b
    5. ON (a.ROWID = b.rid)
    6. WHEN MATCHED THEN
    7. UPDATE
    8. SET
    9. <if test="name != null and name != ''">
    10. NAME =#{name},
    11. <if test="remark != null and remark != ''">
    12. REMARK =#{remark}
    13. WHEN NOT MATCHED THEN INSERT (a.id, a.name, a.remark)
    14. values (#{id}, #{name}, #{remark})

    测试类:

    1. import com.ybst.note.db.comm.dao.testpj.ITestTb1Dao;
    2. import com.ybst.note.db.comm.entity.testpj.TestTb1;
    3. import com.ybst.note.testpj.test.annotation.TargetDataSource;
    4. import org.databene.contiperf.PerfTest;
    5. import org.databene.contiperf.junit.ContiPerfRule;
    6. import org.junit.Before;
    7. import org.junit.Rule;
    8. import org.junit.Test;
    9. import org.junit.runner.RunWith;
    10. import org.slf4j.Logger;
    11. import org.slf4j.LoggerFactory;
    12. import org.springframework.beans.factory.annotation.Autowired;
    13. import org.springframework.boot.test.context.SpringBootTest;
    14. import org.springframework.test.context.junit4.SpringRunner;
    15. import java.util.HashMap;
    16. import java.util.Map;
    17. @SpringBootTest
    18. @RunWith(SpringRunner.class)
    19. public class TestTb1Test {
    20. private static Logger logger = LoggerFactory.getLogger(TestTb1Test.class);
    21. @Rule
    22. public ContiPerfRule contiPerfRule = new ContiPerfRule();
    23. @Autowired
    24. private volatile ITestTb1Dao testTb1Dao;
    25. private int timesRecords = 0;
    26. @TargetDataSource(name = "testpj")
    27. @PerfTest(invocations = 10*10, threads = 10) // 10个线程, 执行 100次, 每个执行10次
    28. @Test
    29. public void testInsertByMerge() {
    30. TestTb1 testTb1 = new TestTb1();
    31. testTb1.setId(1L);
    32. testTb1.setName("zhangsan");
    33. testTb1.setRemark("zhangsan info");
    34. testTb1Dao.insertByMerge(testTb1);
    35. timesRecords++;
    36. logger.info("执行第 {} 次", timesRecords);
    37. }
    38. @Before
    39. public void setup() {
    40. Map paramMap = new HashMap<>();
    41. paramMap.put("id", 1L);
    42. testTb1Dao.deleteByMap(paramMap);
    43. }
    44. }

    执行单元测试后,抛出的异常信息:

    1. SQL> SELECT zz.id FROM TEST_TB1 zz WHERE zz.id = 2;
    2. no rows selected
    3. 2022-07-29 15:12:26.874 INFO 248984 --- [ Thread-8] c.y.n.testpj.test.dao.TestTb1Test : 执行第 74
    4. samples: 74
    5. max: 91334
    6. average: 8659.378378378378
    7. median: 19
    8. org.databene.contiperf.PerfTestExecutionError: org.springframework.dao.DuplicateKeyException:
    9. ### Error updating database. Cause: java.sql.SQLException: ORA-00001: 违反唯一约束条件 (test_db.PK_TEST_TB1)

    6. 小结

    • 使用 merge 时 source table 无数据,会导致 merge 无法正常工作;
    • merge 并发问题也需要注意。
  • 相关阅读:
    百望云携手华为发布金融信创与数电乐企联合方案 创新金融合规变革
    Git 入门到精通
    Seata分布式事务模型和基本应用
    js属性,DOM操作操作标签样式,事件,js事件案例,jQuery库
    读透业务安全白皮书——未来四大趋势
    centos7.x本地挂载阿里云oss
    select() 的 timeout 陷阱
    vue 打印区域内容 vue-print-nb
    ArrayList 和 LinkedList 到底有哪些区别?
    【Acwing1010】拦截导弹(LIS+贪心)题解
  • 原文地址:https://blog.csdn.net/aaronpan21/article/details/126054372