Sqlserver
 sql >> Teknologi Basis Data >  >> RDS >> Sqlserver

Bagaimana cara memperbarui tabel besar dengan jutaan baris di SQL Server?

  1. Anda tidak boleh memperbarui 10k baris dalam satu set kecuali Anda yakin bahwa operasi tersebut mendapatkan Penguncian Halaman (karena beberapa baris per halaman menjadi bagian dari UPDATE operasi). Masalahnya adalah bahwa Eskalasi Penguncian (dari kunci Baris atau Halaman ke Tabel) terjadi pada 5000 kunci . Jadi paling aman untuk menyimpannya di bawah 5000, untuk berjaga-jaga jika operasinya menggunakan Kunci Baris.

  2. Anda seharusnya tidak menggunakan SET ROWCOUNT untuk membatasi jumlah baris yang akan dimodifikasi. Ada dua masalah di sini:

    1. Itu tidak digunakan lagi sejak SQL Server 2005 dirilis (11 tahun yang lalu):

      Menggunakan SET ROWCOUNT tidak akan memengaruhi pernyataan DELETE, INSERT, dan UPDATE dalam rilis SQL Server mendatang. Hindari penggunaan SET ROWCOUNT dengan pernyataan DELETE, INSERT, dan UPDATE dalam pekerjaan pengembangan baru, dan rencanakan untuk memodifikasi aplikasi yang saat ini menggunakannya. Untuk perilaku serupa, gunakan sintaks TOP

    2. Ini dapat memengaruhi lebih dari sekadar pernyataan yang Anda hadapi:

      Menyetel opsi SET ROWCOUNT menyebabkan sebagian besar pernyataan Transact-SQL berhenti memproses ketika telah dipengaruhi oleh jumlah baris yang ditentukan. Ini termasuk pemicu. Opsi ROWCOUNT tidak mempengaruhi kursor dinamis, tetapi membatasi baris kunci dan kursor tidak sensitif. Opsi ini harus digunakan dengan hati-hati.

    Sebagai gantinya, gunakan TOP () klausa.

  3. Tidak ada tujuan melakukan transaksi eksplisit di sini. Ini memperumit kode dan Anda tidak memiliki penanganan untuk ROLLBACK , yang bahkan tidak diperlukan karena setiap pernyataan adalah transaksinya sendiri (yaitu komit otomatis).

  4. Dengan asumsi Anda menemukan alasan untuk menyimpan transaksi eksplisit, maka Anda tidak memiliki TRY / CATCH struktur. Silakan lihat jawaban saya di DBA.StackExchange untuk TRY / CATCH template yang menangani transaksi:

    Apakah kami diharuskan untuk menangani Transaksi dalam Kode C# serta dalam prosedur Store

Saya menduga bahwa yang asli WHERE klausa tidak ditampilkan dalam kode contoh di Pertanyaan, jadi hanya mengandalkan apa yang telah ditampilkan, lebih baik modelnya adalah:

DECLARE @Rows INT,
        @BatchSize INT; -- keep below 5000 to be safe
    
SET @BatchSize = 2000;

SET @Rows = @BatchSize; -- initialize just to enter the loop

BEGIN TRY    
  WHILE (@Rows = @BatchSize)
  BEGIN
      UPDATE TOP (@BatchSize) tab
      SET    tab.Value = 'abc1'
      FROM  TableName tab
      WHERE tab.Parameter1 = 'abc'
      AND   tab.Parameter2 = 123
      AND   tab.Value <> 'abc1' COLLATE Latin1_General_100_BIN2;
      -- Use a binary Collation (ending in _BIN2, not _BIN) to make sure
      -- that you don't skip differences that compare the same due to
      -- insensitivity of case, accent, etc, or linguistic equivalence.

      SET @Rows = @@ROWCOUNT;
  END;
END TRY
BEGIN CATCH
  RAISERROR(stuff);
  RETURN;
END CATCH;

Dengan menguji @Rows terhadap @BatchSize , Anda dapat menghindari UPDATE terakhir itu kueri (dalam banyak kasus) karena set terakhir biasanya beberapa baris kurang dari @BatchSize , dalam hal ini kami tahu bahwa tidak ada lagi yang harus diproses (yang Anda lihat dalam output yang ditunjukkan dalam jawaban Anda). Hanya dalam kasus di mana set baris terakhir sama dengan @BatchSize apakah kode ini akan menjalankan UPDATE terakhir mempengaruhi 0 baris.

Saya juga menambahkan kondisi ke WHERE klausa untuk mencegah baris yang telah diperbarui agar tidak diperbarui lagi.

CATATAN TENTANG KINERJA

Saya menekankan "lebih baik" di atas (seperti dalam, "ini adalah lebih baik model") karena ini memiliki beberapa peningkatan pada kode asli OP, dan berfungsi dengan baik dalam banyak kasus, tetapi tidak sempurna untuk semua kasus. Untuk tabel setidaknya dengan ukuran tertentu (yang bervariasi karena beberapa faktor jadi saya bisa' t lebih spesifik), kinerja akan menurun karena ada lebih sedikit baris yang harus diperbaiki jika:

  1. tidak ada indeks untuk mendukung kueri, atau
  2. ada indeks, tetapi setidaknya ada satu kolom di WHERE klausa adalah tipe data string yang tidak menggunakan susunan biner, oleh karena itu COLLATE klausa ditambahkan ke kueri di sini untuk memaksa pemeriksaan biner, dan hal itu akan membuat indeks tidak valid (untuk kueri khusus ini).

Inilah situasi yang dihadapi @mikesigs, sehingga membutuhkan pendekatan yang berbeda. Metode yang diperbarui menyalin ID untuk semua baris yang akan diperbarui ke tabel sementara, lalu menggunakan tabel temp tersebut untuk INNER JOIN ke tabel yang diperbarui pada kolom kunci indeks berkerumun. (Penting untuk menangkap dan bergabung di indeks berkerumun kolom, apakah itu kolom kunci utama atau bukan!).

Silakan lihat jawaban @mikesigs di bawah ini untuk detailnya. Pendekatan yang ditunjukkan dalam jawaban itu adalah pola yang sangat efektif yang telah saya gunakan sendiri dalam banyak kesempatan. Satu-satunya perubahan yang akan saya buat adalah:

  1. Buat #targetIds secara eksplisit tabel daripada menggunakan SELECT INTO...
  2. Untuk #targetIds tabel, mendeklarasikan kunci utama berkerumun pada kolom.
  3. Untuk #batchIds tabel, mendeklarasikan kunci utama berkerumun pada kolom.
  4. Untuk memasukkan ke dalam #targetIds , gunakan INSERT INTO #targetIds (column_name(s)) SELECT dan hapus ORDER BY karena tidak perlu.

Jadi, jika Anda tidak memiliki indeks yang dapat digunakan untuk operasi ini, dan untuk sementara tidak dapat membuat indeks yang benar-benar berfungsi (indeks yang difilter mungkin berfungsi, tergantung pada WHERE Anda klausa untuk UPDATE query), lalu coba pendekatan yang ditunjukkan dalam jawaban @mikesigs (dan jika Anda menggunakan solusi itu, beri suara lebih tinggi).



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Periksa apakah tabel ada di SQL Server

  2. Cara Menonaktifkan Semua Pemicu di Database SQL Server

  3. Buat Prosedur Tersimpan di SQL Server 2017

  4. Mengapa dianggap praktik buruk menggunakan kursor di SQL Server?

  5. Mengembalikan Daftar Tabel dari Server Tertaut di SQL Server (Contoh T-SQL)