2014-02-20 5 views
4

У меня есть эта таблица в oracle 11g.Oracle Merge - нарушение ограничений по уникальному ключу

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). Вызов слияния происходит из нескольких потоков. Код работает нормально, но вчера мы получили нарушение ограничения (ORA-00001) на уникальном ключе «ORDER_REF_ID».

Есть ли проблема с оператором Merge? или существует ли какой-либо конкретный сценарий, когда вышеуказанный оператор Merge может привести к нарушению ограничения?

Благодаря

Пушкарь

+0

Вы проверили, могут ли два или более потока пытаться вставить тот же order_ref_id? –

ответ

0

Это произойдет, если у вас есть две сессии (в вашем случае, 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); 

(это попробуйте вставить строку, так как сеанс 2 не видит «незафиксированные изменения с сеанса 1. Сессия 2 будет блокироваться, так как она ждет блокировки, удерживаемой сеансом 1):

3) Сессия 1 совершает

=> Сессия 2 Теперь пытается выполнить вставку, которая будет поднимать ORA-00001: UNIQUE CONSTRAINT НАРУШЕНИЕ с ORDER_REF_ID 1 уже существует

UPDATE

Для исправьте эту проблему, я предлагаю вам изменить ваше приложение и ввести некоторую близость между потоками Java и ORDER_REF_IDs - каждый ORDER_REF_ID должен «принадлежать» точно одному потоку, и этот поток должен исключительно вставлять/обновлять данные для своего ORDER_REF_ID.

+0

Спасибо, Фрэнк. Вставка, которую выполняет оператор Merge, является условной, то есть когда ORDER_REF_ID не найден в таблице, она вставляет. Если ORDER_REF_ID найден в таблице, он не выполняет вставку. Я что-то упустил? Слияние было специально реализовано, чтобы избежать ORA-00001. – Prakash

+0

Вы не можете избежать ORA-00001, если два сеанса пытаются вставить одновременно, если вы не измените уровень изоляции транзакций на «READ UNCOMMITTED» (определенно * не рекомендуется *). Самое разумное, что нужно сделать, если ваш MERGE встречает ORA-00001, является ROLLBACK и повторяет попытку операции - если первый сеанс зафиксирован в то же время, второй сеанс не будет пытаться выполнить вставку. –

+0

Эй, Фрэнк, точка Tx имеет смысл. круто. – Prakash

Смежные вопросы