Menggabungkan ukuran limpahan baris dalam SQL Server - "Tidak dapat membuat deretan ukuran .."


8

Tabel target yang saya coba gabungkan data memiliki ~ 660 kolom. Kode untuk gabungan:

MERGE TBL_BM_HSD_SUBJECT_AN_1 AS targetTable
USING
        (                
SELECT * 
FROM TBL_BM_HSD_SUBJECT_AN_1_STAGING
WHERE [ibi_bulk_id] in (20150520141627106) and  id in(101659113)
    ) AS sourceTable 
ON (...)
WHEN MATCHED AND ((targetTable.[sampletime] <= sourceTable.[sampletime]))
        THEN UPDATE SET ...
WHEN NOT MATCHED 
        THEN INSERT (...)
    VALUES (...)

Pertama kali saya menjalankan ini (yaitu ketika tabel kosong) hasilnya berhasil, dan dimasukkan satu baris.

Kedua kalinya saya menjalankan ini, dengan set data yang sama, kesalahan dikembalikan:
Tidak dapat membuat baris ukuran 8410 yang lebih besar dari ukuran baris maksimum yang diijinkan 8060.

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.

Jadi saya mencoba dua hal, (dan berhasil!):

  • Menghapus bagian "KAPAN TIDAK MENCUCI" dari menggabungkan pernyataan
  • Menjalankan pernyataan pembaruan dengan baris yang sama yang saya coba gabungkan

Mengapa pembaruan menggunakan gabungan tidak berhasil, sementara penyisipan berhasil, dan pembaruan langsung juga berhasil?

MEMPERBARUI:

Berhasil menemukan ukuran baris aktual - 4978. Saya telah membuat tabel baru yang hanya memiliki baris ini, dan menemukan ukuran baris dengan cara ini: masukkan deskripsi gambar di sini

Dan saya masih tidak melihat sesuatu yang melebihi batas yang diizinkan.

PEMBARUAN (2):

Reproduksi penuh

Berusaha mereproduksi ini tidak akan memerlukan objek tambahan tambahan, dan bahwa data akan (agak) dikaburkan.

Mencoba ini di beberapa server, dari versi 2012, dan satu dari 2008, dan mampu mereproduksi sepenuhnya pada mereka semua.

Jawaban:


10

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 MERGEimplementasi 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 MERGEskenario 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 ONklausa 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 ONklausul 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:

Sortir Masalah

Perhatikan bahwa Filter memiliki 1.319 kolom (ekspresi dan kolom dasar) dalam Daftar Keluarannya. Melampirkan debugger menunjukkan tumpukan panggilan pada titik pengecualian fatal dimunculkan:

Stack Trace

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 MATCHEDklausa 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 MERGEtugas-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.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.