Asumsikan struktur tabel MyTable(KEY, datafield1, datafield2...)
.
Seringkali saya ingin memperbarui catatan yang ada, atau menyisipkan catatan baru jika tidak ada.
Pada dasarnya:
IF (key exists)
run update command
ELSE
run insert command
Apa cara berkinerja terbaik untuk menulis ini?
Jawaban:
jangan lupa tentang transaksi. Performa bagus, tetapi pendekatan sederhana (JIKA ADA ..) sangat berbahaya.
Ketika beberapa utas akan mencoba melakukan Sisipkan atau perbarui, Anda dapat dengan mudah mendapatkan pelanggaran kunci utama.
Solusi yang disediakan oleh @Beau Crawford & @Esteban menunjukkan gagasan umum tetapi rawan kesalahan.
Untuk menghindari kebuntuan dan pelanggaran PK, Anda dapat menggunakan sesuatu seperti ini:
atau
sumber
Lihat jawaban terinci saya untuk pertanyaan sebelumnya yang sangat mirip
@Beau Crawford's adalah cara yang baik dalam SQL 2005 dan di bawah ini, meskipun jika Anda memberikan perwakilan, ia harus pergi ke orang pertama yang SO . Satu-satunya masalah adalah bahwa untuk menyisipkan masih dua operasi IO.
MS Sql2008 memperkenalkan
merge
dari standar SQL: 2003:Sekarang benar-benar hanya satu operasi IO, tetapi kode mengerikan :-(
sumber
upsert
hanya tentang semua penyedia DB lainnya yang memutuskan untuk mendukungnya. Theupsert
sintaks adalah cara yang jauh lebih baik untuk melakukan hal ini, sehingga setidaknya MS harus didukung juga - itu tidak seperti itu satu-satunya kunci non standar di T-SQLMERGE
sintaksis.HOLDLOCK
operasi penggabungan dalam situasi konkurensi tinggi.Lakukan UPSERT:
http://en.wikipedia.org/wiki/Upsert
sumber
Banyak orang akan menyarankan Anda menggunakannya
MERGE
, tetapi saya memperingatkan Anda untuk tidak menggunakannya. Secara default, itu tidak melindungi Anda dari kondisi konkurensi dan ras lebih dari beberapa pernyataan, dan itu memperkenalkan bahaya lain:http://www.mssqltips.com/sqlservertip/3074/use-caution-with-sql-servers-merge-statement/
Bahkan dengan sintaks "sederhana" ini yang tersedia, saya masih lebih suka pendekatan ini (penanganan kesalahan dihilangkan untuk singkatnya):
Banyak orang akan menyarankan cara ini:
Tetapi semua ini dicapai memastikan Anda mungkin perlu membaca tabel dua kali untuk menemukan baris yang akan diperbarui. Dalam sampel pertama, Anda hanya perlu menemukan baris sekali saja. (Dalam kedua kasus, jika tidak ada baris yang ditemukan dari pembacaan awal, terjadi penyisipan.)
Orang lain akan menyarankan cara ini:
Namun, ini bermasalah jika tidak ada alasan lain selain membiarkan SQL Server menangkap pengecualian yang bisa Anda cegah sejak awal jauh lebih mahal, kecuali dalam skenario langka di mana hampir setiap insert gagal. Saya membuktikan banyak hal di sini:
sumber
UPDATE target SET col = tmp.col FROM target INNER JOIN #tmp ON <key clause>; INSERT target(...) SELECT ... FROM #tmp AS t WHERE NOT EXISTS (SELECT 1 FROM target WHERE key = t.key);
Edit:
Sayangnya, bahkan merugikan saya sendiri, saya harus mengakui solusi yang melakukan ini tanpa memilih tampaknya lebih baik karena mereka menyelesaikan tugas dengan satu langkah lebih sedikit.
sumber
Jika Anda ingin UPSERT lebih dari satu catatan pada suatu waktu Anda dapat menggunakan ANSI SQL: 2003 DML pernyataan MERGE.
Lihat Meniru Pernyataan MERGE di SQL Server 2005 .
sumber
Meskipun cukup terlambat untuk mengomentari ini, saya ingin menambahkan contoh yang lebih lengkap menggunakan MERGE.
Pernyataan Insert + Update tersebut biasanya disebut pernyataan "Upsert" dan dapat diimplementasikan menggunakan MERGE di SQL Server.
Contoh yang sangat baik diberikan di sini: http://weblogs.sqlteam.com/dang/archive/2009/01/31/UPSERT-Race-Condition-With-MERGE.aspx
Di atas juga menjelaskan skenario penguncian dan konkurensi.
Saya akan mengutip yang sama untuk referensi:
sumber
Ganti nama tabel dan bidang dengan apa pun yang Anda butuhkan. Jaga kondisi penggunaan ON . Kemudian atur nilai yang sesuai (dan tipe) untuk variabel-variabel pada baris DECLARE.
Bersulang.
sumber
Anda dapat menggunakan
MERGE
Pernyataan, Pernyataan ini digunakan untuk memasukkan data jika tidak ada atau memperbarui jika memang ada.sumber
Jika melakukan UPDATE jika-tanpa-baris-diperbarui maka rute INSERT, pertimbangkan melakukan INSERT terlebih dahulu untuk mencegah kondisi balapan (dengan asumsi tidak ada HAPUS intervensi)
Selain menghindari kondisi balapan, jika dalam kebanyakan kasus catatan sudah ada maka ini akan menyebabkan INSERT gagal, membuang-buang CPU.
Menggunakan MERGE mungkin lebih baik untuk SQL2008 dan seterusnya.
sumber
Itu tergantung pada pola penggunaan. Kita harus melihat penggunaan gambaran besar tanpa tersesat dalam detailnya. Misalnya, jika pola penggunaannya adalah pembaruan 99% setelah catatan dibuat, maka 'UPSERT' adalah solusi terbaik.
Setelah penyisipan pertama (klik), itu akan menjadi semua pembaruan pernyataan tunggal, tidak ada ifs atau buts. Kondisi 'di mana' pada sisipan diperlukan jika tidak akan memasukkan duplikat, dan Anda tidak ingin berurusan dengan penguncian.
sumber
MS SQL Server 2008 memperkenalkan pernyataan MERGE, yang saya percaya merupakan bagian dari standar SQL: 2003. Seperti yang telah ditunjukkan oleh banyak orang, ini bukan masalah besar untuk menangani kasus satu baris, tetapi ketika berhadapan dengan dataset besar, kita membutuhkan kursor, dengan semua masalah kinerja yang muncul. Pernyataan MERGE akan disambut lebih baik ketika berhadapan dengan dataset besar.
sumber
Sebelum semua orang melompat ke HOLDLOCK-s karena takut dari para pengguna yang susah payah ini menjalankan sprocs Anda secara langsung :-) izinkan saya menunjukkan bahwa Anda harus menjamin keunikan PK-s baru dengan desain (kunci identitas, generator urutan di Oracle, indeks unik untuk ID-s eksternal, kueri yang dicakup oleh indeks). Itulah alfa dan omega dari masalah ini. Jika Anda tidak memilikinya, tidak ada HOLDLOCK-s dari jagat raya yang akan menyelamatkan Anda dan jika Anda memilikinya, maka Anda tidak memerlukan apapun selain UPDLOCK pada pilihan pertama (atau untuk menggunakan pembaruan terlebih dahulu).
Sprocs biasanya berjalan dalam kondisi yang sangat terkendali dan dengan asumsi penelepon tepercaya (mid tier). Berarti bahwa jika pola sederhana sederhana (perbarui + sisipkan atau gabungkan) pernah melihat duplikat PK yang berarti bug pada desain tingkat menengah atau tabel Anda dan ada baiknya SQL akan berteriak kesalahan dalam kasus tersebut dan menolak catatan. Menempatkan HOLDLOCK dalam kasus ini sama dengan pengecualian makan dan mengambil data yang berpotensi salah, selain mengurangi kinerja Anda.
Karena itu, Menggunakan MERGE, atau UPDATE maka INSERT lebih mudah di server Anda dan lebih sedikit kesalahan karena Anda tidak harus ingat untuk menambahkan (UPDLOCK) untuk memilih pertama. Juga, jika Anda melakukan sisipan / pembaruan dalam batch kecil Anda perlu mengetahui data Anda untuk memutuskan apakah suatu transaksi sesuai atau tidak. Itu hanya kumpulan catatan yang tidak terkait maka transaksi "membungkus" tambahan akan merugikan.
sumber
Apakah kondisi perlombaan benar-benar penting jika Anda pertama kali mencoba pembaruan diikuti dengan sisipan? Katakanlah Anda memiliki dua utas yang ingin menetapkan nilai untuk kunci kunci :
Thread 1: value = 1
Thread 2: value = 2
Contoh skenario kondisi balapan
Utas lainnya gagal dengan sisipan (dengan kunci duplikat kesalahan) - utas 2.
Tapi; dalam lingkungan multithreaded, scheduler OS memutuskan urutan eksekusi thread - dalam skenario di atas, di mana kita memiliki kondisi balapan ini, itu adalah OS yang memutuskan urutan eksekusi. Yaitu: Adalah salah untuk mengatakan bahwa "utas 1" atau "utas 2" adalah "pertama" dari sudut pandang sistem.
Ketika waktu eksekusi sangat dekat untuk utas 1 dan utas 2, hasil dari kondisi balapan tidak masalah. Satu-satunya persyaratan adalah salah satu utas harus menentukan nilai yang dihasilkan.
Untuk implementasi: Jika pembaruan diikuti dengan memasukkan hasil kesalahan "kunci duplikat", ini harus dianggap berhasil.
Juga, orang tentu saja tidak boleh berasumsi bahwa nilai dalam database sama dengan nilai yang Anda tulis terakhir.
sumber
Di SQL Server 2008 Anda bisa menggunakan pernyataan MERGE
sumber
Saya telah mencoba solusi di bawah ini dan berfungsi untuk saya, ketika permintaan bersamaan untuk memasukkan pernyataan terjadi.
sumber
Anda dapat menggunakan kueri ini. Bekerja di semua edisi SQL Server. Sederhana, dan jelas. Tetapi Anda perlu menggunakan 2 pertanyaan. Anda dapat menggunakan jika Anda tidak dapat menggunakan MERGE
CATATAN: Tolong jelaskan jawaban negatif
sumber
Jika Anda menggunakan ADO.NET, DataAdapter menangani ini.
Jika Anda ingin menanganinya sendiri, inilah caranya:
Pastikan ada batasan kunci utama pada kolom kunci Anda.
Maka kamu:
Anda juga dapat melakukannya dengan cara lain, yaitu melakukan penyisipan terlebih dahulu, dan melakukan pembaruan jika penyisipan gagal. Biasanya cara pertama lebih baik, karena pembaruan dilakukan lebih sering daripada menyisipkan.
sumber
Melakukan jika ada ... yang lain ... melibatkan melakukan dua permintaan minimum (satu untuk memeriksa, satu untuk mengambil tindakan). Pendekatan berikut ini hanya membutuhkan satu di mana catatan itu ada, dua jika memasukkan diperlukan:
sumber
Saya biasanya melakukan apa yang dikatakan oleh beberapa poster lain sehubungan dengan memeriksa keberadaannya terlebih dahulu dan kemudian melakukan apa pun jalur yang benar. Satu hal yang harus Anda ingat ketika melakukan ini adalah bahwa rencana eksekusi yang di-cache oleh sql bisa menjadi tidak optimal untuk satu jalur atau yang lainnya. Saya percaya cara terbaik untuk melakukan ini adalah memanggil dua prosedur tersimpan yang berbeda.
Sekarang, saya tidak terlalu sering mengikuti saran saya sendiri, jadi ambillah dengan sebutir garam.
sumber
Lakukan pilih, jika Anda mendapatkan hasil, perbarui, jika tidak, buatlah.
sumber