Saya mencoba memperbarui tabel dengan array nilai. Setiap item dalam array berisi informasi yang cocok dengan baris dalam tabel di database SQL Server. Jika baris sudah ada dalam tabel, kami memperbarui baris itu dengan informasi dalam array yang diberikan. Selain itu, kami menyisipkan baris baru di tabel. Saya pada dasarnya menggambarkan upert.
Sekarang, saya mencoba untuk mencapai ini dalam prosedur tersimpan yang mengambil parameter XML. Alasan saya menggunakan XML dan bukan param tabel-nilai adalah karena, melakukan yang terakhir, saya harus membuat jenis kustom dalam SQL dan mengaitkan jenis ini dengan prosedur yang tersimpan. Jika saya pernah mengubah sesuatu dalam prosedur tersimpan saya atau skema db saya di jalan, saya harus mengulang prosedur tersimpan dan jenis kustom. Saya ingin menghindari situasi ini. Selain itu, keunggulan TVP lebih dari XML tidak berguna untuk situasi saya karena, ukuran array data saya tidak akan pernah melebihi 1000. Ini berarti saya tidak dapat menggunakan solusi yang diusulkan di sini: Cara memasukkan beberapa catatan menggunakan XML dalam SQL server 2008
Juga, diskusi serupa di sini ( UPSERT - Apakah ada alternatif yang lebih baik untuk MERGE atau @@ rowcount? ) Berbeda dari apa yang saya tanyakan karena, saya mencoba untuk menaikkan beberapa baris ke sebuah tabel.
Saya berharap bahwa saya hanya akan menggunakan set kueri berikut untuk memberikan nilai dari xml. Tapi ini tidak akan berhasil. Pendekatan ini seharusnya hanya berfungsi ketika input adalah satu baris.
begin tran
update table with (serializable) set select * from xml_param
where key = @key
if @@rowcount = 0
begin
insert table (key, ...) values (@key,..)
end
commit tran
Alternatif berikutnya adalah menggunakan JIKA ADA lengkap atau salah satu variasi dari bentuk berikut. Tapi, saya menolak ini dengan alasan efisiensi yang tidak optimal:
IF (SELECT COUNT ... ) > 0
UPDATE
ELSE
INSERT
Opsi selanjutnya menggunakan pernyataan Gabung seperti yang dijelaskan di sini: http://www.databasejournal.com/features/mssql/using-the-merge-statement-to-perform-an-upsert.html . Tapi, kemudian saya membaca tentang masalah dengan permintaan Gabung di sini: http://www.mssqltips.com/sqlservertip/3074/use-caution-with-sql-servers-merge-statement/ . Untuk alasan ini, saya berusaha menghindari Penggabungan.
Jadi, sekarang pertanyaan saya adalah: apakah ada opsi lain atau cara yang lebih baik untuk mencapai beberapa upsert menggunakan parameter XML dalam prosedur tersimpan SQL Server 2008?
Harap dicatat bahwa data dalam parameter XML dapat berisi beberapa catatan yang tidak boleh di-UPSERT karena lebih tua dari catatan saat ini. Ada ModifiedDate
bidang di XML dan tabel tujuan yang perlu dibandingkan untuk menentukan apakah catatan harus diperbarui atau dibuang.
sumber
MERGE
yang ditunjukkan oleh Bertrand sebagian besar adalah kasus tepi dan ketidakefisienan, bukan penghenti - MS tidak akan melepaskannya jika itu adalah ladang ranjau yang nyata. Apakah Anda yakin bahwa konvolusi yang Anda hindariMERGE
tidak membuat lebih banyak kesalahan potensial daripada yang disimpan?MERGE
. Langkah-langkah INSERT dan UPDATE dari MERGE masih diproses secara terpisah. Perbedaan utama dalam pendekatan saya adalah variabel tabel yang menyimpan ID rekaman yang diperbarui dan permintaan DELETE yang menggunakan variabel tabel itu untuk menghapus catatan-catatan itu dari tabel temp dari data yang masuk. Dan saya kira SUMBER bisa langsung dari @ XMLparam.nodes () daripada membuang ke tabel temp, tapi tetap saja, itu tidak banyak hal tambahan untuk tidak perlu khawatir tentang pernah menemukan diri Anda dalam salah satu kasus tepi; - ).Jawaban:
Apakah sumbernya XML atau TVP tidak membuat perbedaan besar. Operasi keseluruhan pada dasarnya adalah:
Anda melakukannya dalam urutan itu karena jika Anda MASUKKAN terlebih dahulu, maka semua baris ada untuk mendapatkan PEMBARUAN dan Anda akan melakukan pekerjaan berulang untuk setiap baris yang baru saja dimasukkan.
Di luar itu ada berbagai cara untuk mencapai hal ini dan berbagai cara untuk menyesuaikan beberapa efisiensi tambahan darinya.
Mari kita mulai dengan minimum. Karena mengekstraksi XML kemungkinan menjadi salah satu bagian yang lebih mahal dari operasi ini (jika bukan yang paling mahal), kami tidak ingin harus melakukannya dua kali (karena kami memiliki dua operasi untuk melakukan). Jadi, kami membuat tabel temp dan mengekstrak data dari XML ke dalamnya:
Dari sana kami melakukan PEMBARUAN dan kemudian MASUK:
Sekarang setelah operasi dasar tidak berfungsi, kami dapat melakukan beberapa hal untuk mengoptimalkan:
ambil @@ ROWCOUNT dari insert ke tabel temp dan bandingkan dengan @@ ROWCOUNT dari UPDATE. Jika mereka sama maka kita dapat melewati INSERT
ambil nilai ID yang diperbarui melalui klausa OUTPUT dan HAPUS nilai-nilai dari tabel temp. Maka INSERT tidak membutuhkan
WHERE NOT EXISTS(...)
JIKA ada baris dalam data yang masuk yang tidak boleh disinkronkan (yaitu tidak dimasukkan atau diperbarui), maka catatan-catatan itu harus dihapus sebelum melakukan UPDATE
Saya telah menggunakan model ini beberapa kali pada Impor / ETL yang memiliki lebih dari 1000 baris atau mungkin 500 dalam batch dari total 20k - lebih dari satu juta baris. Namun, saya belum menguji perbedaan kinerja antara DELETE dari baris yang diperbarui dari tabel temp vs hanya memperbarui bidang [IsUpdate].
Harap perhatikan tentang keputusan untuk menggunakan XML over TVP karena ada, paling banyak, 1000 baris untuk diimpor sekaligus (disebutkan dalam pertanyaan):
Jika ini dipanggil beberapa kali di sana-sini, maka sangat mungkin kenaikan kinerja minor di TVP mungkin tidak sebanding dengan biaya pemeliharaan tambahan (perlu membatalkan proc sebelum mengubah Jenis Tabel yang Didefinisikan Pengguna, perubahan kode aplikasi, dll) . Tetapi jika Anda mengimpor 4 juta baris, mengirimkan 1000 sekaligus, yaitu 4000 eksekusi (dan 4 juta baris XML untuk diuraikan tidak peduli bagaimana itu dipecah), dan bahkan perbedaan kinerja kecil ketika dijalankan hanya beberapa kali akan tambahkan hingga perbedaan yang nyata.
Yang sedang berkata, metode seperti yang saya jelaskan tidak berubah di luar mengganti SELECT FROM @XmlInputParam menjadi SELECT FROM @TVP. Karena TVP hanya baca, Anda tidak akan dapat menghapusnya. Saya kira Anda bisa menambahkan sebuah
WHERE NOT EXISTS(SELECT * FROM @UpdateIDs ids WHERE ids.IDField = tmp.IDField)
ke SELECT akhir (diikat ke INSERT) bukan sederhanaWHERE IsUpdate = 0
. Jika Anda menggunakan@UpdateIDs
variabel tabel dengan cara ini, maka Anda bahkan bisa lolos dengan tidak membuang baris yang masuk ke tabel temp.sumber