Mengapa kedua kalinya saya mencoba untuk menggabungkan baris yang sama yang sudah dimasukkan itu menghasilkan kesalahan. Jika baris ini melebihi ukuran baris maksimum, itu diharapkan tidak memungkinkan untuk memasukkannya di tempat pertama.
Pertama, terima kasih untuk skrip reproduksi.
Masalahnya bukan bahwa SQL Server tidak dapat menyisipkan atau memperbarui baris yang terlihat oleh pengguna tertentu . Seperti yang Anda catat, baris yang telah dimasukkan ke tabel tentu tidak bisa terlalu besar untuk ditangani oleh SQL Server.
Masalahnya terjadi karena MERGE
implementasi SQL Server menambahkan informasi yang dihitung (sebagai kolom tambahan) selama langkah-langkah menengah dalam rencana eksekusi. Informasi tambahan ini diperlukan untuk alasan teknis, untuk melacak apakah setiap baris harus menghasilkan sisipan, pembaruan, atau penghapusan; dan juga terkait dengan cara SQL Server secara umum menghindari pelanggaran kunci sementara selama perubahan indeks.
SQL Server Storage Engine memerlukan indeks untuk menjadi unik (secara internal, termasuk setiap pemersatu yang tersembunyi) setiap saat - karena setiap baris diproses - daripada pada awal dan akhir transaksi lengkap. Dalam MERGE
skenario yang lebih kompleks , ini membutuhkan Pemisahan (mengubah pembaruan menjadi penghapusan dan penyisipan terpisah), Sortir, dan Ciutkan opsional (mengubah sisipan dan pembaruan yang berdekatan pada tombol yang sama menjadi pembaruan). Informasi lebih lanjut .
Sebagai tambahan, perhatikan bahwa masalah tidak terjadi jika tabel target adalah tumpukan (letakkan indeks berkerumun untuk melihat ini). Saya tidak merekomendasikan ini sebagai perbaikan, hanya menyebutkannya untuk menyoroti hubungan antara mempertahankan keunikan indeks setiap saat (dikelompokkan dalam kasus ini), dan Split-Sort-Collapse.
Dalam kueri sederhana MERGE
, dengan indeks unik yang sesuai , dan hubungan langsung antara baris sumber dan target (biasanya cocok menggunakan ON
klausa yang menampilkan semua kolom kunci), pengoptimal kueri dapat menyederhanakan banyak logika generik, menghasilkan rencana yang relatif sederhana yang dilakukan tidak memerlukan Split-Sort-Collapse, atau Segment-Sequence Project untuk memeriksa bahwa baris target hanya disentuh satu kali.
Dalam pertanyaan kompleks MERGE
, dengan lebih banyak logika buram, pengoptimal biasanya tidak dapat menerapkan penyederhanaan ini, mengekspos lebih banyak dari logika kompleks yang diperlukan untuk pemrosesan yang benar (terlepas dari bug produk, dan ada banyak ).
Permintaan Anda tentu memenuhi syarat sebagai kompleks. The ON
klausul tidak cocok dengan kunci indeks (dan saya mengerti mengapa), dan 'sumber tabel' adalah diri bergabung melibatkan fungsi jendela peringkat (sekali lagi, dengan alasan):
MERGE MERGE_REPRO_TARGET AS targetTable
USING
(
SELECT * FROM
(
SELECT
*,
ROW_NUMBER() OVER (
PARTITION BY ww,id, tenant
ORDER BY
(
SELECT COUNT(1)
FROM MERGE_REPRO_SOURCE AS targetTable
WHERE
targetTable.[ibi_bulk_id] = sourceTable.[ibi_bulk_id]
AND targetTable.[ibi_row_id] <> sourceTable.[ibi_row_id]
AND
(
(targetTable.[ww] = sourceTable.[ww])
AND (targetTable.[id] = sourceTable.[id])
AND (targetTable.[tenant] = sourceTable.[tenant])
)
AND NOT ((targetTable.[sampletime] <= sourceTable.[sampletime]))
),
sourceTable.ibi_row_id DESC
) AS idx
FROM MERGE_REPRO_SOURCE sourceTable
WHERE [ibi_bulk_id] in (20150803110418887)
) AS bulkData
where idx = 1
) AS sourceTable
ON
(targetTable.[ww] = sourceTable.[ww])
AND (targetTable.[id] = sourceTable.[id])
AND (targetTable.[tenant] = sourceTable.[tenant])
...
Ini menghasilkan banyak kolom tambahan yang dikomputasi, terutama yang terkait dengan Split dan data yang diperlukan ketika pembaruan dikonversi ke pasangan sisipan / pembaruan. Kolom tambahan ini menghasilkan baris antara yang melebihi 8.060 byte yang diizinkan pada Sort sebelumnya - yang hanya setelah Filter:
Perhatikan bahwa Filter memiliki 1.319 kolom (ekspresi dan kolom dasar) dalam Daftar Keluarannya. Melampirkan debugger menunjukkan tumpukan panggilan pada titik pengecualian fatal dimunculkan:
Perhatikan secara sepintas bahwa masalahnya bukan pada Spool - pengecualian ada dikonversi menjadi peringatan tentang potensi baris menjadi terlalu besar.
Mengapa pembaruan menggunakan gabungan tidak berhasil, sementara penyisipan berhasil, dan pembaruan langsung juga berhasil?
Pembaruan langsung tidak memiliki kompleksitas internal yang sama dengan MERGE
. Ini adalah operasi yang pada dasarnya lebih sederhana yang cenderung menyederhanakan dan mengoptimalkan dengan lebih baik. Menghapus NOT MATCHED
klausa juga dapat menghapus cukup kompleksitas sehingga kesalahan tidak dihasilkan dalam beberapa kasus. Namun itu tidak terjadi dengan repro.
Pada akhirnya, saran saya adalah untuk menghindari MERGE
tugas-tugas yang lebih besar atau lebih kompleks. Pengalaman saya adalah bahwa pernyataan insert / update / delete yang terpisah cenderung untuk mengoptimalkan lebih baik, lebih sederhana untuk dipahami, dan juga sering berkinerja lebih baik secara keseluruhan, dibandingkan dengan MERGE
.