【问题标题】:Oracle Merge - constraint violation on unique keyOracle Merge - 唯一键上的约束违规
【发布时间】:2014-02-20 09:48:43
【问题描述】:

我在 oracle 11g 中有这个表。

TABLE: ORDER_LOCK
Name                   Null     Type                
---------------------- -------- ----------          
ORDER_ID                     NOT NULL NUMBER(10) [PRIMARY KEY] 
ORDER_REF_ID                 NUMBER(10)          [UNIQUE KEY]
ORDER_MSG_SENT                   NUMBER(1)          


merge into ORDER_LOCK al 
using ( select ? ORDER_REF_ID, ? ORDER_MSG_SENT from dual ) t 
on (al.ORDER_REF_ID = t.ORDER_REF_ID) 
when not matched then 
    insert (ORDER_ID, ORDER_REF_ID, ORDER_MSG_SENT) 
    values (ORDER_LOCK_SEQ.nextval, t.ORDER_REF_ID, t.ORDER_MSG_SENT)

我正在从我的应用程序 (Java) 中使用上述合并。合并调用来自多个线程。代码一直运行良好,但昨天我们在唯一键“ORDER_REF_ID”上遇到约束违规 (ORA-00001)。

Merge 语句有问题吗?或者,上面的 Merge 语句可能会导致违反约束,是否有任何特定场景?

谢谢

普什卡

【问题讨论】:

  • 您是否通过尝试插入相同的 order_ref_id 来检查两个或多个线程是否可能?

标签: sql oracle oracle11g


【解决方案1】:

如果您有两个会话(在您的情况下是 Java 线程)尝试插入相同的 ORDER_REF_ID,就会发生这种情况。考虑以下场景:

1) 会话 1 执行此 MERGE 语句(不提交):

merge into ORDER_LOCK al 
using ( select 1 ORDER_REF_ID, sysdate ORDER_MSG_SENT from dual ) t 
on (al.ORDER_REF_ID = t.ORDER_REF_ID) 
when not matched then 
    insert (ORDER_ID, ORDER_REF_ID, ORDER_MSG_SENT) 
    values (ORDER_LOCK_SEQ.nextval, t.ORDER_REF_ID, t.ORDER_MSG_SENT);

2) 会话 2 启动相同的 MERGE 语句:

merge into ORDER_LOCK al 
using ( select 1 ORDER_REF_ID, sysdate ORDER_MSG_SENT from dual ) t 
on (al.ORDER_REF_ID = t.ORDER_REF_ID) 
when not matched then 
    insert (ORDER_ID, ORDER_REF_ID, ORDER_MSG_SENT) 
    values (ORDER_LOCK_SEQ.nextval, t.ORDER_REF_ID, t.ORDER_MSG_SENT);

(这将尝试插入行,因为 Session 2 没有“看到”来自 Session 1 的未提交更改。Session 2 将阻塞,因为它正在等待 Session 1 持有的锁):

3) 会话 1 提交

=> 会话 2 现在尝试执行插入,这将引发 ORA-00001: UNIQUE CONSTRAINT VIOLATION 因为 ORDER_REF_ID 1 已经存在

更新

要解决这个问题,我建议您修改您的应用程序并在 Java 线程和 ORDER_REF_ID 之间引入某种亲和性 - 每个 ORDER_REF_ID 应该“属于”一个线程,并且该线程应该专门为其插入/更新数据ORDER_REF_ID。

【讨论】:

  • 谢谢弗兰克。 Merge 语句正在执行的插入是有条件的,即当在 table 中找不到 ORDER_REF_ID 时,它会执行插入。如果在表中找到 ORDER_REF_ID,它不会进行插入。我在这里错过了什么吗?合并是专门为避免 ORA-00001 而实施的。
  • 如果两个会话同时尝试插入,则无法避免 ORA-00001,除非您将事务隔离级别更改为“未提交读”(绝对推荐)。如果您的 MERGE 遇到 ORA-00001,最明智的做法是 ROLLBACK 并重试该操作 - 如果同时已提交第一个会话,则第二个会话将不会尝试执行插入。
  • 你对markhoxey.wordpress.com/2015/04/15/…有什么看法?
猜你喜欢
  • 2018-06-28
  • 1970-01-01
  • 1970-01-01
  • 2022-11-11
  • 1970-01-01
  • 2022-01-23
  • 1970-01-01
  • 1970-01-01
  • 2022-09-23
相关资源
最近更新 更多