Saya mencoba menggunakan MERGE
pernyataan untuk menyisipkan atau menghapus baris dari sebuah tabel, tetapi saya hanya ingin bertindak pada subset dari baris-baris itu. Dokumentasi untuk MERGE
memiliki peringatan yang cukup kuat:
Penting untuk menentukan hanya kolom dari tabel target yang digunakan untuk tujuan pencocokan. Yaitu, tentukan kolom dari tabel target yang dibandingkan dengan kolom yang sesuai dari tabel sumber. Jangan mencoba meningkatkan kinerja kueri dengan memfilter baris di tabel target dalam klausa ON, seperti dengan menentukan DAN TIDAK target_table.column_x = nilai. Melakukannya dapat mengembalikan hasil yang tidak terduga dan salah.
tetapi inilah yang tampaknya harus saya lakukan untuk membuat MERGE
pekerjaan saya .
Data yang saya miliki adalah tabel standar banyak-ke-banyak yang menggabungkan item ke kategori (misalnya item mana yang termasuk dalam kategori mana) seperti:
CategoryId ItemId
========== ======
1 1
1 2
1 3
2 1
2 3
3 5
3 6
4 5
Yang perlu saya lakukan adalah mengganti semua baris dalam kategori tertentu secara efektif dengan daftar item baru. Upaya awal saya untuk melakukan ini terlihat seperti ini:
MERGE INTO CategoryItem AS TARGET
USING (
SELECT ItemId FROM SomeExternalDataSource WHERE CategoryId = 2
) AS SOURCE
ON SOURCE.ItemId = TARGET.ItemId AND TARGET.CategoryId = 2
WHEN NOT MATCHED BY TARGET THEN
INSERT ( CategoryId, ItemId )
VALUES ( 2, ItemId )
WHEN NOT MATCHED BY SOURCE AND TARGET.CategoryId = 2 THEN
DELETE ;
Tampaknya ini berfungsi dalam pengujian saya, tetapi saya melakukan persis apa yang MSDN peringatkan agar saya tidak melakukannya. Ini membuat saya khawatir bahwa saya akan mengalami masalah yang tidak terduga nanti, tetapi saya tidak dapat melihat cara lain untuk membuat MERGE
satu - satunya baris yang memengaruhi saya dengan nilai bidang spesifik ( CategoryId = 2
) dan mengabaikan baris dari kategori lain.
Apakah ada cara "yang lebih benar" untuk mencapai hasil yang sama? Dan apa "hasil yang tidak terduga atau tidak benar" yang MSDN memperingatkan saya tentang?
sumber
Jawaban:
The
MERGE
pernyataan memiliki sintaks yang kompleks dan bahkan lebih kompleks pelaksanaan, tapi pada dasarnya idenya adalah untuk bergabung dengan dua tabel, filter ke baris yang perlu diubah (dimasukkan, diperbarui, atau dihapus), dan kemudian untuk melakukan perubahan yang diminta. Diberikan data sampel berikut:Target
Sumber
Hasil yang diinginkan adalah mengganti data dalam target dengan data dari sumber, tetapi hanya untuk
CategoryId = 2
. Dengan mengikuti uraian yangMERGE
diberikan di atas, kita harus menulis kueri yang menggabungkan sumber dan target pada kunci saja, dan menyaring baris hanya dalamWHEN
klausa:Ini memberikan hasil sebagai berikut:
Rencana pelaksanaannya adalah:
Perhatikan bahwa kedua tabel dipindai sepenuhnya. Kami mungkin menganggap ini tidak efisien, karena hanya baris yang
CategoryId = 2
akan terpengaruh dalam tabel target. Di sinilah peringatan di Books Online masuk. Salah satu upaya salah kaprah untuk mengoptimalkan hanya menyentuh baris yang diperlukan dalam target adalah:Logika dalam
ON
klausa diterapkan sebagai bagian dari gabungan. Dalam hal ini, join adalah join luar penuh (lihat entri Books Online ini untuk alasannya). Menerapkan tanda centang untuk kategori 2 pada baris target sebagai bagian dari gabungan luar akhirnya menghasilkan baris dengan nilai yang berbeda dihapus (karena mereka tidak cocok dengan sumber):Akar penyebabnya adalah alasan yang sama mengapa predikat berperilaku berbeda di
ON
klausa join luar daripada yang mereka lakukan jika ditentukan dalamWHERE
klausa. TheMERGE
sintaks (dan pelaksanaan bergabung tergantung pada klausul tertentu) hanya membuat lebih sulit untuk melihat bahwa ini adalah begitu.The bimbingan dalam buku Online (diperluas dalam Kinerja Mengoptimalkan entry) menawarkan panduan yang akan menjamin semantik yang benar dinyatakan menggunakan
MERGE
sintaks, tanpa pengguna harus harus memahami semua rincian pelaksanaan, atau akun untuk cara di mana optimizer mungkin sah mengatur ulang hal-hal untuk alasan efisiensi eksekusi.Dokumentasi menawarkan tiga cara potensial untuk menerapkan penyaringan awal:
Menentukan kondisi pemfilteran dalam
WHEN
klausa menjamin hasil yang benar, tetapi dapat berarti bahwa lebih banyak baris dibaca dan diproses dari tabel sumber dan target daripada yang sangat diperlukan (seperti yang terlihat pada contoh pertama).Memutakhirkan melalui tampilan yang berisi kondisi pemfilteran juga menjamin hasil yang benar (karena baris yang diubah harus dapat diakses untuk pembaruan melalui tampilan) tetapi ini memang memerlukan tampilan khusus, dan yang mengikuti kondisi aneh untuk memperbarui tampilan.
Menggunakan ekspresi tabel umum memiliki risiko yang sama dengan menambahkan predikat ke
ON
klausa, tetapi untuk alasan yang sedikit berbeda. Dalam banyak kasus itu akan aman, tetapi membutuhkan analisis ahli dari rencana eksekusi untuk mengkonfirmasi ini (dan pengujian praktis yang luas). Sebagai contoh:Ini menghasilkan hasil yang benar (tidak diulang) dengan rencana yang lebih optimal:
Paket hanya membaca baris untuk kategori 2 dari tabel target. Ini mungkin pertimbangan kinerja yang penting jika tabel target besar, tetapi terlalu mudah untuk mendapatkan kesalahan ini menggunakan
MERGE
sintaks.Terkadang, lebih mudah untuk menulis
MERGE
operasi DML yang terpisah. Pendekatan ini bahkan dapat berkinerja lebih baik daripada tunggalMERGE
, sebuah fakta yang sering mengejutkan orang.sumber