Sisipkan jika tidak ada, secara bersamaan

13

Saya mengalami masalah konkurensi dengan sisipan saya dalam prosedur tersimpan. Bagian yang relevan dari prosedur ini adalah:

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

Ketika kami menjalankan 3 atau 4 dari proc yang disimpan ini secara bersamaan, kami mendapatkan beberapa insert sesekali.

Saya berencana memperbaiki ini seperti ini:

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

Pertanyaannya adalah, apakah cara memasukkan secara bersamaan tanpa duplikat di server sql? Fakta bahwa saya harus menggunakan TOP hanya untuk memasukkan sekali mengganggu saya.

Chris
sumber
1
Anda tidak harus menggunakan TOP. Hapus referensi tabel FROM dari pernyataan SELECT.
ErikE
@ GS Saya pikir Anda benar.
Chris

Jawaban:

7

Anda bisa menggunakan pernyataan gabungan dengan serializablepetunjuk.

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);
Mikael Eriksson
sumber
Apakah Anda stres menguji pendekatan Anda dari dua koneksi atau lebih?
AK
2
@AlexKuznetsov - Saya melakukannya beberapa saat yang lalu untuk pertanyaan lain pada SO. Saya menggunakan dua tab di SSMS. Pertama-tama menguji insert ... where not exist ...pola dan menemukan bahwa Anda bisa mendapatkan kebuntuan dan pelanggaran utama sehingga diperlukan untuk menggunakan updlock dan serial. Saya kemudian menguji pernyataan gabungan dan berpikir itu akan menangani hal-hal yang sedikit lebih baik dan itu terjadi karena di sana tidak ada deadlock tetapi saya masih harus menggunakan serializable untuk tidak memiliki pelanggaran utama.
Mikael Eriksson
1
Ini jawaban yang sangat luar biasa.
Chris Marisic
5

Jika Anda tidak ingin duplikat pada kolom 'nilai lain', Anda dapat melakukannya dengan membuat unique constraintkolom tersebut. Kueri akan menjadi:

 ALTER TABLE table1
 ADD CONSTRAINT unique_c_othervalue UNIQUE(othervalue)

Ini akan mengembalikan kesalahan jika kueri mencoba memasukkan nilai duplikat ke kolom 'nilai lain'.

StanleyJohns
sumber
Bagaimana cara kerjanya jika kendala uniknya adalah tuple dua baris?
Chris
1
@ Chris Bagaimana Anda memiliki batasan unik yang menjangkau baris?
Aaron Bertrand
@ Harun Saya mungkin mematikan terminologi saya, tetapi kami memiliki dua baris yang bersama-sama harus unik. Saya tidak berpikir itu diberlakukan dalam skema kami.
Chris
2

Gunakan batasan unik seperti rekomendasi @StanleyJohns. Kemudian gunakan BEGIN TRY END TRY di sekitar pernyataan insert Anda.

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
mrdenny
sumber