Oracle Merge - 对唯一键的约束冲突

时间:2014-02-20 09:48:44

标签: sql oracle oracle11g

我在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)中使用上面的Merge。合并调用发生在多个线程中。代码一直工作正常但昨天我们在唯一键'ORDER_REF_ID'上遇到了约束违规(ORA-00001)。

Merge语句有问题吗?或者,当上述Merge语句可能导致约束违规时,是否存在任何特定情况?

由于

普什卡

1 个答案:

答案 0 :(得分:0)

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

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);

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

3)第1节提交

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

<强>更新

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