Anda dapat menggunakan LOCKs untuk membuat hal-hal SERIALIZABLE tetapi ini mengurangi konkurensi. Mengapa tidak mencoba kondisi umum terlebih dahulu ("kebanyakan masukkan atau sebagian besar pilih") diikuti dengan penanganan tindakan "perbaikan" yang aman? Yaitu, pola "JFDI"...
Sebagian besar diharapkan INSERT (taman bola 70-80%+):
Coba masukkan saja. Jika gagal, baris telah dibuat. Tidak perlu khawatir tentang konkurensi karena TRY/CATCH menangani duplikat untuk Anda.
BEGIN TRY
INSERT Table VALUES (@Value)
SELECT @id = SCOPE_IDENTITY()
END TRY
BEGIN CATCH
IF ERROR_NUMBER() <> 2627
RAISERROR etc
ELSE -- only error was a dupe insert so must already have a row to select
SELECT @id = RowID FROM Table WHERE RowValue = @VALUE
END CATCH
Sebagian besar PILIHAN:
Mirip, tapi coba ambil datanya dulu. Tidak ada data =INSERT diperlukan. Sekali lagi, jika 2 panggilan bersamaan coba INSERT karena keduanya menemukan baris tidak memiliki tuas TRY/CATCH.
BEGIN TRY
SELECT @id = RowID FROM Table WHERE RowValue = @VALUE
IF @@ROWCOUNT = 0
BEGIN
INSERT Table VALUES (@Value)
SELECT @id = SCOPE_IDENTITY()
END
END TRY
BEGIN CATCH
IF ERROR_NUMBER() <> 2627
RAISERROR etc
ELSE
SELECT @id = RowID FROM Table WHERE RowValue = @VALUE
END CATCH
Yang ke-2 tampaknya berulang, tetapi sangat bersamaan. Kunci akan mencapai hal yang sama tetapi dengan mengorbankan konkurensi...
Sunting:
Mengapa tidak untuk menggunakan MERGE...
Jika Anda menggunakan klausa OUTPUT itu hanya akan mengembalikan apa yang diperbarui. Jadi, Anda memerlukan UPDATE dummy untuk menghasilkan tabel INSERTED untuk klausa OUTPUT. Jika Anda harus melakukan pembaruan dummy dengan banyak panggilan (seperti yang tersirat oleh OP) yang banyak menulis log hanya untuk dapat menggunakan MERGE.