2016-07-07 1 views
1

Давайте представлю, что мы имеем некоторую хорошую, простую таблицу:Операторы Oracle MERGE, выполняемые в параллельных транзакциях: как предотвратить дублирование ключей?

CREATE TABLE blah (
    some_key VARCHAR(32), 
    some_value VARCHAR(32) 
); 

По какой-то причине мы не хотим, чтобы определить первичный ключ или уникальный индекс на some_key колонки.

Теперь мы выполняем в MERGE отчетности в параллельных транзакциях (т.е. второго один запускаются перед первым совершенным):

MERGE INTO blah blah 
USING (SELECT 'some_key' some_key, 'a_value' some_value FROM DUAL) rec 
ON (blah.some_key = rec.some_key) 
WHEN MATCHED THEN 
UPDATE SET blah.some_value = rec.some_value 
WHEN NOT MATCHED THEN 
INSERT (blah.some_key, blah.some_value) VALUES (rec.some_key, rec.some_value); 

и:

... 
USING (SELECT 'some_key' some_key, 'another_value' some_value FROM DUAL) rec 
... 

После совершения одновременно SELECT показывает следующие:

| SOME_KEY | SOME_VALUE | 
|----------|---------------| 
| some_key | a_value  | 
| some_key | another_value | 

Я понимаю, e механика за этим (т. два оператора слияния не «видят» друг друга), но это как-то явно не желаемый результат.

Конечно, я могу определить ключ UNIQUE. Затем происходит второй этап, что также объяснимо, но как-то неожиданно из заявления MERGE.

Есть ли способ сделать MERGE сделать «реальный» MERGE без каких-либо дубликатов «ключей»?

+2

No. в параллельный сценарий, вы не можете предотвратить дублирование 100% без каких-либо уникальных ограничений. Если вы не возражаете размещать блокировку на всей таблице в течение всей транзакции, которая (маловероятна). – sstan

+0

Нет, замок на весь стол на самом деле не то, что я ищу ;-) –

+0

Я так не думал :). Но я отправил ответ, который может быть вам полезен. – sstan

ответ

2

Если имеет смысл создать уникальное ограничение на some_key, тогда вы действительно должны его создать. Это единственный способ полностью предотвратить дублирование ключей.

Помимо создания уникального ограничения, это звучит так, как вы на самом деле после этого, должно быть в состоянии сериализовать операторы слияния. Другими словами, чтобы гарантировать, что 2 одновременных пользователя никогда не смогут действительно запускать оба оператора merge одновременно. Скорее, нужно подождать, пока другой не завершится, без хлопот одного из пользователей, получивших ошибку.

Вот идея достижения этой цели, которая не включает блокировку всей таблицы, в которую вы сливаетесь. Вы можете создать отдельную таблицу, разработанную специально для сериализации целых операций. Затем вы помещаете блокировку в определенную строку этой таблицы, которая представляет вашу операцию, используя select ... for update. Это, по сути, сериализует доступ к оператору merge. И затем, после того, как вы выполните команду merge, вы совершаете транзакцию, чтобы позволить другим выполнять также merge, но только после того, как изменения станут видимыми для других транзакций.

Вот пример сценария, чтобы дать вам идею:

Настройка

create table operation_lock (
    operation_name varchar2(50) not null 
) 
/

alter table operation_lock 
add constraint operation_lock_pk 
primary key (operation_name) 
/

insert into operation_lock (operation_name) values ('my_merge_operation') 
/

commit 
/

Как бы запустить merge операцию в транзакции

select * from operation_lock 
where operation_name = 'my_merge_operation' 
    for update 
/

merge into blah blah... 
/

commit 
/
+0

Да, сериализация - это ключевое слово. Приятно слышать, что есть возможности решить эту проблему. –

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