Вставте, якщо його немає, одночасно


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? Мене турбує той факт, що мені потрібно використовувати TOP для вставки лише один раз.


1
Вам не доведеться використовувати TOP. Видаліть посилання таблиці FROM з оператора SELECT.
ЕрікЕ


@GSerg Я думаю, ти маєш рацію.
Кріс

Відповіді:


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

Ви піддавали стрес-тестуванню свій підхід із двох чи більше зв’язків?
АК

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

1
Це справді приголомшлива відповідь.
Кріс Марісіч

5

Якщо ви не хочете дублікатів у стовпці "інше значення", ви можете зробити це, створивши в unique constraintцьому стовпці. Запит буде таким:

 ALTER TABLE table1
 ADD CONSTRAINT unique_c_othervalue UNIQUE(othervalue)

Це призведе до усунення помилки, якби запит намагався вставити повторне значення у стовпчик "othervalue".


Як би це діяло, якщо єдиним обмеженням є дворядний кортеж?
Кріс

1
@Chris Як у вас є унікальне обмеження, яке охоплює рядки?
Аарон Бертран

@Aaron У мене, мабуть, вимкнено свою термінологію, але у нас є два ряди, які разом повинні бути унікальними. Я не думаю, що це застосовується в нашій схемі.
Кріс

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.