Вставьте, если не существует, одновременно


13

У меня проблемы с параллелизмом при вставке в хранимую процедуру. Соответствующая часть процедуры такова:

select @_id = Id from table1 where othervalue = @_othervalue
IF( @_id IS NULL)
BEGIN
    insert into table1 (othervalue) values (@_othervalue)
    select @_id = Id from table1 where othervalue = @_othervalue
END

Когда мы запускаем 3 или 4 из этих сохраненных процедур одновременно, мы иногда получаем несколько вставок.

Я планирую исправить это так:

insert into table1 (othervalue) 
    select TOP(1) @_othervalue as othervalue from table1 WITH(UPDLOCK) 
    where NOT EXISTS ( select * from table1 where othervalue = @_othervalue )

select @_id = Id from table1 where othervalue = @_othervalue

Вопрос в том, как одновременно вставлять без дубликатов в SQL Server? Меня беспокоит тот факт, что я должен использовать ТОП, чтобы вставить только один раз.


1
Вам не нужно использовать TOP. Удалите ссылку на таблицу FROM из оператора SELECT.
ErikE


@Gerg Я думаю, ты прав.
Крис

Ответы:


7

Вы можете использовать оператор слияния с serializableподсказкой.

merge table1 with (serializable) as T 
using (select @_othervalue as othervalue) as S
on T.othervalue = S.othervalue
when not matched then
  insert (othervalue) values (othervalue);

Вы стресс-тестировали свой подход из двух или более соединений?
AK

2
@AlexKuznetsov - Я сделал это некоторое время назад для другого вопроса о SO. Я использовал две вкладки в SSMS. Сначала протестировал insert ... where not exist ...шаблон и обнаружил, что вы можете получить взаимоблокировки и нарушения ключа, поэтому там было необходимо использовать Updlock и сериализуемые. Затем я протестировал оператор слияния и подумал, что он будет обрабатывать вещи немного лучше, и он сделал это потому, что там, где нет тупиков, но мне все еще приходилось использовать сериализуемый код, чтобы не было нарушений ключа.
Микаэль Эрикссон

1
Это действительно потрясающий ответ.
Крис Марисик

5

Если вам не нужны дубликаты в столбце «othervalue», вы можете сделать это, создав в unique constraintэтом столбце. Запрос будет:

 ALTER TABLE table1
 ADD CONSTRAINT unique_c_othervalue UNIQUE(othervalue)

Это выдаст ошибку, если запрос попытается вставить повторяющееся значение в столбец «другое значение».


Как это будет работать, если уникальным ограничением является кортеж из двух строк?
Крис

1
@Chris Как у вас есть уникальное ограничение, которое охватывает строки?
Аарон Бертран

@ Аарон У меня, наверное, нет терминологии, но у нас есть две строки, которые должны быть уникальными. Я не думаю, что это применяется в нашей схеме.
Крис

2

Используйте уникальное ограничение, которое рекомендует @StanleyJohns. Затем используйте BEGIN TRY END TRY вокруг вашего оператора вставки.

select @_id = Id from table1 where othervalue = @_othervalue
IF( @_id IS NULL)
BEGIN
    BEGIN TRY
        insert into table1 (othervalue) values (@_othervalue)
        select @_id = Id from table1 where othervalue = @_othervalue        
    END TRY
    BEGIN CATCH
        select @_id = Id from table1 where othervalue = @_othervalue        
    END CATCH
END
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.