Kita sering menghadapi situasi "Jika tidak ada, masukkan". Blog Dan Guzman memiliki penyelidikan yang sangat baik tentang bagaimana membuat proses ini threadsafe.
Saya punya tabel dasar yang hanya katalog string ke integer dari a SEQUENCE
. Dalam prosedur tersimpan saya harus mendapatkan kunci integer untuk nilai jika ada, atau INSERT
kemudian mendapatkan nilai yang dihasilkan. Ada batasan keunikan pada dbo.NameLookup.ItemName
kolom sehingga integritas data tidak berisiko tetapi saya tidak ingin menemukan pengecualian.
Ini bukan IDENTITY
jadi saya tidak bisa mendapatkan SCOPE_IDENTITY
dan nilainya bisa NULL
dalam kasus tertentu.
Dalam situasi saya, saya hanya harus berurusan dengan INSERT
keselamatan di atas meja, jadi saya mencoba untuk memutuskan apakah itu praktik yang lebih baik untuk digunakan MERGE
seperti ini:
SET NOCOUNT, XACT_ABORT ON;
DECLARE @vValueId INT
DECLARE @inserted AS TABLE (Id INT NOT NULL)
MERGE
dbo.NameLookup WITH (HOLDLOCK) AS f
USING
(SELECT @vName AS val WHERE @vName IS NOT NULL AND LEN(@vName) > 0) AS new_item
ON f.ItemName= new_item.val
WHEN MATCHED THEN
UPDATE SET @vValueId = f.Id
WHEN NOT MATCHED BY TARGET THEN
INSERT
(ItemName)
VALUES
(@vName)
OUTPUT inserted.Id AS Id INTO @inserted;
SELECT @vValueId = s.Id FROM @inserted AS s
Saya bisa melakukan ini tanpa menggunakan MERGE
hanya dengan persyaratan INSERT
diikuti oleh SELECT
saya pikir pendekatan kedua ini lebih jelas bagi pembaca, tapi saya tidak yakin itu "lebih baik" latihan
SET NOCOUNT, XACT_ABORT ON;
INSERT INTO
dbo.NameLookup (ItemName)
SELECT
@vName
WHERE
NOT EXISTS (SELECT * FROM dbo.NameLookup AS t WHERE @vName IS NOT NULL AND LEN(@vName) > 0 AND t.ItemName = @vName)
DECLARE @vValueId int;
SELECT @vValueId = i.Id FROM dbo.NameLookup AS i WHERE i.ItemName = @vName
Atau mungkin ada cara lain yang lebih baik yang belum saya pertimbangkan
Saya melakukan pencarian dan referensi pertanyaan lain. Yang ini: /programming/5288283/sql-server-insert-if-not-exists-best-practice adalah yang paling tepat yang bisa saya temukan tetapi tampaknya tidak terlalu berlaku untuk kasus penggunaan saya. Pertanyaan lain untuk IF NOT EXISTS() THEN
pendekatan yang menurut saya tidak bisa diterima.
sumber
Jawaban:
Karena Anda menggunakan Urutan, Anda dapat menggunakan fungsi NEXT VALUE FOR yang sama - yang sudah Anda miliki di Batasan Default pada
Id
bidang Kunci Utama - untuk menghasilkanId
nilai baru sebelumnya. Menghasilkan nilai terlebih dahulu berarti Anda tidak perlu khawatir tidak memilikiSCOPE_IDENTITY
, yang berarti Anda tidak perluOUTPUT
klausa atau melakukan tambahanSELECT
untuk mendapatkan nilai baru; Anda akan memiliki nilai sebelum melakukannyaINSERT
, dan Anda bahkan tidak perlu dipusingkan denganSET IDENTITY INSERT ON / OFF
:-)Sehingga itu mengurus sebagian dari keseluruhan situasi. Bagian lainnya adalah menangani masalah konkurensi dari dua proses, pada saat yang bersamaan, tidak menemukan baris yang ada untuk string yang sama persis, dan melanjutkan dengan
INSERT
. Kekhawatirannya adalah tentang menghindari pelanggaran Batasan Unik yang akan terjadi.Salah satu cara untuk menangani masalah konkurensi ini adalah dengan memaksa operasi khusus ini menjadi utas tunggal. Cara untuk melakukannya adalah dengan menggunakan kunci aplikasi (yang bekerja lintas sesi). Meskipun efektif, mereka bisa agak sulit untuk situasi seperti ini di mana frekuensi tabrakan mungkin cukup rendah.
Cara lain untuk menangani tabrakan adalah dengan menerima bahwa mereka kadang-kadang akan terjadi dan menanganinya daripada mencoba menghindarinya. Dengan menggunakan
TRY...CATCH
konstruk, Anda dapat secara efektif menjebak kesalahan spesifik (dalam kasus ini: "pelanggaran kendala unik", Msg 2601) dan menjalankan kembaliSELECT
untuk mendapatkanId
nilai karena kami tahu bahwa sekarang ada karena berada diCATCH
blok dengan tertentu kesalahan. Kesalahan lainnya dapat ditangani dengan cara yang khasRAISERROR
/RETURN
atauTHROW
.Pengaturan Tes: Urutan, Tabel, dan Indeks Unik
Pengaturan Tes: Prosedur Tersimpan
Ujian
Pertanyaan dari OP
MERGE
memiliki berbagai "masalah" (beberapa referensi ditautkan dalam jawaban @ SqlZim sehingga tidak perlu menggandakan info itu di sini). Dan, tidak ada penguncian tambahan dalam pendekatan ini (kurang pertikaian), jadi itu harus lebih baik pada konkurensi. Dalam pendekatan ini Anda tidak akan pernah mendapatkan pelanggaran Kendala Unik, semua tanpa adaHOLDLOCK
, dll. Ini cukup banyak dijamin untuk bekerja.Alasan di balik pendekatan ini adalah:
CATCH
blok di tempat pertama akan sangat rendah. Lebih masuk akal untuk mengoptimalkan kode yang akan menjalankan 99% dari waktu daripada kode yang akan berjalan 1% dari waktu (kecuali tidak ada biaya untuk mengoptimalkan keduanya, tetapi itu tidak terjadi di sini).Komentar dari jawaban @ SqlZim (penekanan ditambahkan)
Saya akan setuju dengan kalimat pertama ini jika diubah untuk menyatakan "dan _ ketika bijaksana". Hanya karena sesuatu mungkin secara teknis tidak berarti bahwa situasi (yaitu kasus penggunaan yang dimaksudkan) akan diuntungkan olehnya.
Masalah yang saya lihat dengan pendekatan ini adalah bahwa ia mengunci lebih dari apa yang disarankan. Penting untuk membaca kembali dokumentasi yang dikutip pada "serializable", khususnya yang berikut (penekanan ditambahkan):
Sekarang, inilah komentar dalam kode contoh:
Kata operatif ada "rentang". Kunci yang diambil tidak hanya pada nilai in
@vName
, tetapi lebih tepatnya rentang mulai darilokasi di mana nilai baru ini harus pergi (yaitu antara nilai-nilai kunci yang ada di kedua sisi di mana nilai baru cocok), tetapi bukan nilai itu sendiri. Artinya, proses lain akan diblokir dari memasukkan nilai baru, tergantung pada nilai yang sedang dicari. Jika pencarian dilakukan di bagian atas rentang, maka memasukkan apa pun yang bisa menempati posisi yang sama akan diblokir. Misalnya, jika nilai "a", "b", dan "d" ada, maka jika satu proses melakukan SELECT pada "f", maka tidak mungkin untuk memasukkan nilai "g" atau bahkan "e" ( karena salah satu dari mereka akan datang segera setelah "d"). Tetapi, memasukkan nilai "c" akan dimungkinkan karena tidak akan ditempatkan dalam rentang "dicadangkan".Contoh berikut harus menggambarkan perilaku ini:
(Di tab permintaan (yaitu Sesi) # 1)
(Di tab permintaan (yaitu Sesi) # 2)
Demikian juga, jika nilai "C" ada, dan nilai "A" dipilih (dan karenanya dikunci), maka Anda dapat memasukkan nilai "D", tetapi bukan nilai "B":
(Di tab permintaan (yaitu Sesi) # 1)
(Di tab permintaan (yaitu Sesi) # 2)
Agar adil, dalam pendekatan yang saya sarankan, ketika ada pengecualian, akan ada 4 entri dalam Log Transaksi yang tidak akan terjadi dalam pendekatan "transaksi yang dapat diubah serial" ini. TETAPI, seperti yang saya katakan di atas, jika pengecualian terjadi 1% (atau bahkan 5%) pada saat itu, itu jauh lebih sedikit berdampak daripada kasus SELECT awal yang jauh lebih mungkin untuk sementara waktu memblokir operasi INSERT.
Masalah lain, meskipun kecil, dengan pendekatan "transaksi serializable + klausa OUTPUT" ini adalah
OUTPUT
klausa (dalam penggunaannya saat ini) mengirim data kembali sebagai hasil yang ditetapkan. Set hasil memerlukan lebih banyak overhead (mungkin di kedua sisi: di SQL Server untuk mengelola kursor internal, dan di lapisan aplikasi untuk mengelola objek DataReader) daripadaOUTPUT
parameter sederhana . Mengingat bahwa kita hanya berurusan dengan nilai skalar tunggal, dan bahwa asumsi adalah frekuensi eksekusi yang tinggi, overhead tambahan dari set hasil mungkin bertambah.Sementara
OUTPUT
klausa dapat digunakan sedemikian rupa untuk mengembalikanOUTPUT
parameter, itu akan membutuhkan langkah-langkah tambahan untuk membuat tabel sementara atau variabel tabel, dan kemudian untuk memilih nilai dari variabel temp / tabel tabel ke dalamOUTPUT
parameter.Klarifikasi Lebih Lanjut: Tanggapan atas Tanggapan @ SqlZim (jawaban yang diperbarui) terhadap Tanggapan saya terhadap Tanggapan @ SqlZim (dalam jawaban asli) terhadap pernyataan saya mengenai konkurensi dan kinerja ;-)
Maaf jika bagian ini agak lama, tetapi pada titik ini kita hanya sampai pada nuansa dari dua pendekatan.
Ya, saya akan mengakui bahwa saya bias, meskipun harus adil:
INSERT
gagal karena pelanggaran Kendala Unik. Saya belum melihat yang disebutkan di salah satu jawaban / posting lainnya.Mengenai pendekatan @ gbn tentang "JFDI", posting Michael J. Swart "Ugly Pragmatism For The Win", dan komentar Aaron Bertrand pada posting Michael (mengenai tesnya menunjukkan skenario apa yang telah menurunkan kinerja), dan komentar Anda tentang "adaptasi Michael J" Anda Adaptasi Stewart tentang prosedur Try Catch JFDI @ gbn "yang menyatakan:
Sehubungan dengan diskusi gbn / Michael / Aaron yang terkait dengan pendekatan "JFDI", akan salah untuk menyamakan saran saya dengan pendekatan "JFDI" gbn. Karena sifat operasi "Dapatkan atau Sisipkan", ada kebutuhan eksplisit untuk melakukan hal tersebut
SELECT
untuk mendapatkanID
nilai catatan yang ada. SELECT ini bertindak sebagaiIF EXISTS
cek, yang membuat pendekatan ini lebih sesuai dengan variasi "CheckTryCatch" dari pengujian Aaron. Kode yang ditulis ulang Michael (dan adaptasi terakhir Anda dari adaptasi Michael) juga termasukWHERE NOT EXISTS
untuk melakukan pemeriksaan yang sama terlebih dahulu. Karenanya, saran saya (bersama dengan kode akhir Michael dan adaptasi Anda terhadap kode terakhirnya) tidak akan benar-benar menghantamCATCH
blok terlalu sering. Itu hanya bisa situasi di mana dua sesi,ItemName
INSERT...SELECT
pada saat yang sama persis sehingga kedua sesi menerima "benar" untukWHERE NOT EXISTS
pada saat yang sama dan dengan demikian keduanya berusaha melakukanINSERT
pada saat yang sama persis. Skenario yang sangat spesifik itu terjadi jauh lebih jarang daripada memilih yang sudah adaItemName
atau menyisipkan yang baruItemName
ketika tidak ada proses lain yang berusaha melakukannya pada saat yang sama persis .DENGAN SEMUA YANG DI ATAS DALAM PIKIRAN: Mengapa saya lebih suka pendekatan saya?
Pertama, mari kita lihat apa yang terjadi dengan penguncian dalam pendekatan "serializable". Seperti disebutkan di atas, "rentang" yang dikunci tergantung pada nilai kunci yang ada di kedua sisi di mana nilai kunci baru cocok. Awal atau akhir rentang juga bisa menjadi awal atau akhir indeks, jika tidak ada nilai kunci yang ada di arah itu. Asumsikan kita memiliki indeks dan kunci berikut (
^
mewakili awal indeks sementara$
mewakili akhir dari itu):Jika sesi 55 mencoba memasukkan nilai kunci:
A
, maka rentang # 1 (dari^
keC
) dikunci: sesi 56 tidak dapat memasukkan nilaiB
, meskipun unik dan valid (belum). Tapi sesi 56 dapat memasukkan nilai-nilaiD
,G
danM
.D
, maka rentang # 2 (dariC
keF
) dikunci: sesi 56 tidak dapat memasukkan nilaiE
(belum). Tapi sesi 56 dapat memasukkan nilai-nilaiA
,G
danM
.M
, maka rentang # 4 (dariJ
ke$
) dikunci: sesi 56 tidak dapat memasukkan nilaiX
(belum). Tapi sesi 56 dapat memasukkan nilai-nilaiA
,D
danG
.Semakin banyak nilai-nilai kunci yang ditambahkan, rentang antara nilai-nilai kunci menjadi lebih sempit, sehingga mengurangi probabilitas / frekuensi dari beberapa nilai yang dimasukkan pada saat yang sama memperebutkan rentang yang sama. Memang, ini bukan masalah besar , dan untungnya itu tampaknya menjadi masalah yang benar-benar berkurang dari waktu ke waktu.
Masalah dengan pendekatan saya dijelaskan di atas: itu hanya terjadi ketika dua sesi mencoba untuk memasukkan nilai kunci yang sama pada saat yang sama. Dalam hal ini turun ke apa yang memiliki probabilitas lebih tinggi terjadi: dua nilai kunci berbeda, namun dekat, dicoba pada saat yang sama, atau nilai kunci yang sama dicoba pada waktu yang sama? Saya kira jawabannya terletak pada struktur aplikasi melakukan sisipan, tetapi secara umum saya akan menganggapnya lebih mungkin bahwa dua nilai berbeda yang kebetulan berbagi rentang yang sama sedang dimasukkan. Tetapi satu-satunya cara untuk benar-benar tahu adalah dengan menguji keduanya pada sistem OPs.
Selanjutnya, mari kita pertimbangkan dua skenario dan bagaimana masing-masing pendekatan menanganinya:
Semua permintaan adalah untuk nilai kunci unik:
Dalam hal ini,
CATCH
blok di saran saya tidak pernah dimasukkan, maka tidak ada "masalah" (yaitu 4 entri log tran dan waktu yang diperlukan untuk melakukan itu). Tetapi, dalam pendekatan "serializable", bahkan dengan semua sisipan menjadi unik, akan selalu ada beberapa potensi untuk memblokir sisipan lain dalam rentang yang sama (meskipun tidak terlalu lama).Frekuensi tinggi permintaan untuk nilai kunci yang sama pada saat yang sama:
Dalam hal ini - tingkat keunikan yang sangat rendah dalam hal permintaan masuk untuk nilai-nilai kunci yang tidak ada -
CATCH
blok dalam saran saya akan dimasukkan secara teratur. Efek dari hal ini adalah bahwa setiap sisipan yang gagal akan perlu untuk memutar kembali secara otomatis dan menulis 4 entri ke Log Transaksi, yang merupakan sedikit performa yang memukul setiap kali. Tetapi operasi keseluruhan seharusnya tidak pernah gagal (setidaknya bukan karena ini).(Ada masalah dengan versi sebelumnya dari pendekatan "diperbarui" yang memungkinkannya untuk mengalami kebuntuan. Sebuah
updlock
petunjuk ditambahkan untuk mengatasi ini dan itu tidak lagi mendapatkan kebuntuan.)TETAPI, dalam pendekatan "serializable" (bahkan versi yang diperbarui dan dioptimalkan), operasi akan menemui jalan buntu. Mengapa? Karenaserializable
perilaku hanya mencegahINSERT
operasi dalam kisaran yang telah dibaca dan karenanya dikunci; itu tidak mencegahSELECT
operasi pada kisaran itu.The
serializable
pendekatan, dalam hal ini, tampaknya akan memiliki overhead tambahan, dan mungkin melakukan sedikit lebih baik dari apa yang saya sarankan.Seperti dengan banyak / sebagian besar diskusi mengenai kinerja, karena terdapat begitu banyak faktor yang dapat mempengaruhi hasil, satu-satunya cara untuk benar-benar memiliki perasaan tentang bagaimana sesuatu akan dilakukan adalah dengan mencobanya di lingkungan target di mana ia akan berjalan. Pada titik itu tidak akan menjadi masalah pendapat :).
sumber
Jawaban yang Diperbarui
Tanggapan untuk @srutzky
Saya setuju, dan untuk alasan yang sama saya menggunakan parameter output saat bijaksana . Adalah kesalahan saya untuk tidak menggunakan parameter output pada jawaban awal saya, saya malas.
Berikut adalah prosedur yang direvisi menggunakan parameter output, optimasi tambahan, bersama dengan
next value for
yang @srutzky jelaskan dalam jawabannya :catatan pembaruan : Termasuk
updlock
dengan pilih akan mengambil kunci yang tepat dalam skenario ini. Terima kasih kepada @srutzky, yang menunjukkan bahwa ini dapat menyebabkan deadlock ketika hanya digunakanserializable
padaselect
.Catatan: Ini mungkin bukan kasusnya, tetapi jika memungkinkan prosedur akan dipanggil dengan nilai untuk
@vValueId
, termasukset @vValueId = null;
setelahnyaset xact_abort on;
, jika tidak maka dapat dihapus.Tentang contoh @ srutzky tentang perilaku penguncian rentang kunci:
@srutzky hanya menggunakan satu nilai di tabelnya, dan mengunci kunci "berikutnya" / "tak terbatas" untuk pengujiannya untuk menggambarkan penguncian rentang kunci. Sementara tesnya menggambarkan apa yang terjadi dalam situasi itu, saya percaya cara informasi disajikan dapat mengarah pada asumsi yang salah tentang jumlah penguncian yang bisa dihadapi seseorang ketika menggunakan
serializable
dalam skenario seperti yang disajikan dalam pertanyaan asli.Meskipun saya merasakan bias (mungkin salah) dalam cara dia menyajikan penjelasan dan contoh penguncian rentang kunci, mereka masih benar.
Setelah penelitian lebih lanjut, saya menemukan artikel blog yang sangat relevan dari 2011 oleh Michael J. Swart: Mythbusting: Pembaruan Serentak / Solusi Penyisipan . Di dalamnya, ia menguji beberapa metode untuk akurasi dan konkurensi. Metode 4: Peningkatan Isolasi + Kunci Tuning Halus didasarkan pada posting Sam Saffron Masukkan atau Perbarui Pola Untuk SQL Server , dan satu-satunya metode dalam tes asli untuk memenuhi harapannya (bergabung kemudian dengan
merge with (holdlock)
).Pada bulan Februari 2016, Michael J. Swart memposting Ugly Pragmatism For The Win . Dalam postingan itu, ia membahas beberapa penyetelan tambahan yang ia buat untuk prosedur peringatan Saffron untuk mengurangi penguncian (yang saya sertakan dalam prosedur di atas).
Setelah melakukan perubahan itu, Michael tidak senang prosedurnya mulai terlihat lebih rumit dan berkonsultasi dengan seorang rekan bernama Chris. Chris membaca semua posting Mythbusters asli dan membaca semua komentar dan bertanya tentang pola TRY CATCH JFDI @ gbn . Pola ini mirip dengan jawaban @ srutzky, dan merupakan solusi yang akhirnya Michael gunakan dalam contoh itu.
Michael J Swart:
Menurut pendapat saya, kedua solusi itu layak. Walaupun saya masih lebih suka untuk meningkatkan level isolasi dan kunci fine tune, jawaban @ srutzky juga valid dan mungkin atau mungkin tidak lebih performan dalam situasi spesifik Anda.
Mungkin di masa depan saya juga akan sampai pada kesimpulan yang sama dengan Michael J. Swart, tapi saya belum sampai di sana.
Ini bukan pilihan saya, tapi inilah adaptasi saya terhadap adaptasi Michael J. Stewart dari @gbn's Try Catch. Prosedur JFDI akan terlihat seperti:
Jika Anda lebih sering memasukkan nilai baru daripada memilih nilai yang ada, ini mungkin lebih berkinerja daripada versi @ srutzky . Kalau tidak, saya lebih suka versi @ srutzky daripada yang ini.
Komentar Aaron Bertrand tentang tautan posting Michael J Swart ke pengujian yang relevan yang telah dilakukannya dan mengarah ke pertukaran ini. Kutipan dari bagian komentar pada Pragmatisme Jelek Untuk Kemenangan :
dan balasan dari:
Tautan baru:
Jawaban asli
Saya masih lebih suka menggunakan pendekatan Sam Saffron vs menggunakan
merge
, terutama ketika berhadapan dengan satu baris.Saya akan mengadaptasi metode yang bagus itu untuk situasi ini seperti ini:
Saya akan konsisten dengan penamaan Anda, dan
serializable
sama sepertiholdlock
, pilih satu dan konsisten dalam penggunaannya. Saya cenderung menggunakanserializable
karena itu nama yang sama dengan yang digunakan saat menentukanset transaction isolation level serializable
.Dengan menggunakan
serializable
atauholdlock
kunci rentang diambil berdasarkan nilai@vName
yang membuat operasi lain menunggu jika mereka memilih atau memasukkan nilai ke dalamdbo.NameLookup
yang menyertakan nilai dalamwhere
klausa.Agar kunci rentang berfungsi dengan baik, perlu ada indeks pada
ItemName
kolom ini berlaku saat menggunakanmerge
juga.Berikut adalah prosedur yang akan terlihat kebanyakan mengikuti whitepaper Erland Sommarskog untuk penanganan kesalahan , menggunakan
throw
. Jikathrow
bukan bagaimana Anda meningkatkan kesalahan, ubahlah agar konsisten dengan prosedur lainnya:Untuk meringkas apa yang terjadi dalam prosedur di atas:
set nocount on; set xact_abort on;
seperti yang selalu Anda lakukan , maka jika variabel input kamiis null
atau kosong,select id = cast(null as int)
sebagai hasilnya. Jika tidak nol atau kosong, dapatkanId
variabel kami sambil memegang tempat itu kalau-kalau tidak ada. JikaId
ada, kirimkan. Jika tidak ada, masukkan dan kirimkan yang baruId
.Sementara itu, panggilan lain ke prosedur ini mencoba untuk menemukan Id untuk nilai yang sama akan menunggu sampai transaksi pertama selesai dan kemudian pilih & kembalikan. Panggilan lain ke prosedur ini atau pernyataan lain yang mencari nilai lain akan terus berlanjut karena ini tidak menghalangi.
Walaupun saya setuju dengan @srutzky bahwa Anda dapat menangani tabrakan dan menelan pengecualian untuk masalah semacam ini, saya pribadi lebih suka mencoba dan menyesuaikan solusi untuk menghindari hal itu jika memungkinkan. Dalam hal ini, saya tidak merasa bahwa menggunakan kunci dari
serializable
adalah pendekatan yang berat, dan saya yakin itu akan menangani konkurensi tinggi dengan baik.Kutipan dari dokumentasi sql server pada tabel mengisyaratkan
serializable
/holdlock
:Kutipan dari dokumentasi sql server pada tingkat isolasi transaksi
serializable
Tautan terkait dengan solusi di atas:
Sisipkan atau Perbarui pola untuk Sql Server - Sam Saffron
Dokumentasi pada Petunjuk Tableizable serial dan lainnya - MSDN
Penanganan Kesalahan dan Transaksi di SQL Server Bagian Satu - Penanganan Kesalahan Jumpstart - Erland Sommarskog
Saran Erland Sommarskog tentang @@ rowcount , (yang tidak saya ikuti dalam contoh ini).
MERGE
memiliki sejarah jerawatan, dan tampaknya butuh lebih banyak waktu untuk memastikan bahwa kode tersebut berperilaku seperti yang Anda inginkan di bawah semua sintaks itu.merge
Artikel yang relevan :Bug MERGE yang Menarik - Paul White
Kondisi Balap UPSERT Dengan Gabung - sqlteam
Gunakan Perhatian dengan Pernyataan MERGE SQL Server - Aaron Bertrand
Bisakah saya mengoptimalkan pernyataan gabungan ini - Aaron Bertrand
Jika Anda menggunakan tampilan yang diindeks dan MERGE, baca ini! - Aaron Bertrand
Satu tautan terakhir, Kendra Little melakukan perbandingan kasar
merge
vsinsert with left join
, dengan peringatan di mana ia berkata "Saya tidak melakukan pengujian beban menyeluruh tentang ini", tetapi masih merupakan bacaan yang baik.sumber