Bagaimana cara menjalankan skrip besar dengan banyak sisipan tanpa kehabisan memori?

28

Pertanyaan:

Saya memiliki skrip dengan sekitar 45 ribu sisipan dari pernyataan pilih. Ketika saya mencoba dan menjalankannya, saya mendapatkan pesan kesalahan yang menyatakan bahwa saya kehabisan memori. Bagaimana saya bisa menjalankan skrip ini?

Konteks:

  1. Menambahkan beberapa bidang data baru untuk membuat aplikasi bermain bagus dengan aplikasi lain yang digunakan klien.
  2. Mendapat spreadsheet data dari klien yang penuh dengan data yang memetakan item data saat ini ke nilai untuk bidang baru ini.
  3. Spreadsheet yang dikonversi untuk menyisipkan pernyataan.
  4. Jika saya hanya menjalankan beberapa pernyataan itu berfungsi tetapi seluruh skrip tidak.
  5. Tidak. Tidak ada kesalahan ketik.

Jika ada cara yang berbeda saya harus memuat data ini merasa bebas untuk menghukum saya dan memberi tahu saya.

spaghetticowboy
sumber
Pertanyaan serupa pada SO: ( stackoverflow.com/questions/222442/… ) Tidak yakin apakah jawabannya membantu
jumpdart

Jawaban:

17

Ukuran batch maksimum untuk SQL Server 2005 adalah 65.536 * Ukuran Paket Jaringan (NPS), di mana NPS biasanya 4KB. Itu berhasil hingga 256 MB. Itu berarti bahwa pernyataan insert Anda akan rata-rata 5,8 KB masing-masing. Itu kelihatannya tidak benar, tapi mungkin ada ruang asing atau sesuatu yang tidak biasa di sana.

Saran pertama saya adalah meletakkan pernyataan "GO" setelah setiap pernyataan INSERT. Ini akan memecah batch tunggal Anda dari 45.000 laporan INSERT menjadi 45.000 batch terpisah. Ini seharusnya lebih mudah dicerna. Hati-hati, jika salah satu sisipan gagal, Anda mungkin kesulitan menemukan pelakunya. Anda mungkin ingin melindungi diri Anda dengan transaksi. Anda dapat menambahkan pernyataan tersebut dengan cepat jika editor Anda memiliki pencarian dan penggantian yang baik (yang akan memungkinkan Anda mencari dan mengganti karakter yang dikembalikan seperti \ r \ n) atau fasilitas makro.

Saran kedua adalah menggunakan Wisaya untuk mengimpor data langsung dari Excel. Wisaya membuat paket SSIS kecil untuk Anda, di belakang layar, dan kemudian menjalankannya. Itu tidak akan memiliki masalah ini.

selat darin
sumber
2
A GOsetelah setiap pernyataan? Yah, saya kira jika Anda membuat mereka menggunakan skrip lain tidak apa-apa. Kalau tidak, saya hanya akan menempatkan satu setelah setiap 1000 INSERTs. Berkenaan dengan membuat transaksi atomik dan meminimalkan ukuran transaksi, mengapa tidak memuat semua baris ke tabel temp atau variabel tabel dan kemudian memuatnya dalam satu tembakan dari sana ke tabel target?
Nick Chammas
1000 sama baiknya dengan 1, tetapi lebih sulit untuk dihitung. Sejujurnya, dia mungkin lolos hanya dengan satu pernyataan GO, di tengah jalan, dekat pernyataan 21.500. Saya suka perbaikan GO karena tidak memerlukan pengeditan rumit skrip saat ini, atau menghitung pernyataan INSERT (yang mungkin tidak memetakan langsung ke nomor baris).
Selat darin
2
Tentunya, bahkan perkiraan buruk dari 1000 pernyataan sudah cukup baik. :)
Nick Chammas
1
Menambahkan GO adalah perbaikan cepat dan mudah .. Script 25MB berjalan dalam waktu kurang dari 9 menit tanpa masalah. Ingin memilikinya sebagai skrip untuk menyimpannya dalam proses penerapan tambalan standar kami saat keluar.
spaghetticowboy
14

BULK INSERT atau bcp tampaknya opsi yang lebih tepat daripada 45.000 pernyataan insert.

Jika Anda harus tetap menggunakan pernyataan insert, saya akan mempertimbangkan beberapa opsi:

A: Gunakan transaksi dan bungkus batch 100 atau 500 atau 1000 pernyataan di masing-masing untuk meminimalkan dampak pada log dan batch. misalnya

BEGIN TRANSACTION;
INSERT dbo.table(a, ...) SELECT 1, ...
INSERT dbo.table(a, ...) SELECT 2, ...
...
INSERT dbo.table(a, ...) SELECT 500, ...
COMMIT TRANSACTION;
GO

BEGIN TRANSACTION;
INSERT dbo.table(a, ...) SELECT 1, ...
INSERT dbo.table(a, ...) SELECT 2, ...
...
INSERT dbo.table(a, ...) SELECT 500, ...
COMMIT TRANSACTION;
GO

B: Alih-alih pernyataan penyisipan individual, gunakan UNION ALLuntuk 100 atau 500 pernyataan sekaligus, mis

INSERT dbo.table(a, ...)
SELECT 1, ...
UNION ALL SELECT 2, ...
...
UNION ALL SELECT 500, ...
GO

INSERT dbo.table(a, ...)
SELECT 501, ...
UNION ALL SELECT 502, ...
...
UNION ALL SELECT 1000, ...
GO

Saya telah meninggalkan kesalahan penanganan untuk singkatnya, tetapi intinya adalah bahwa saya tidak akan pernah mencoba mengirim batch tunggal 45.000 laporan individu ke SQL Server.

Aaron Bertrand
sumber
1
Sayang sekali OP tidak bisa menggunakan konstruktor nilai-tabel , fitur 2008+. Dia masih harus mengelompokkan sisipan ke dalam kelompok 1000 baris, yang merupakan jumlah maksimum yang dapat Anda kelompokkan bersama dengan TVC.
Nick Chammas
Itu akan menjadi saran pertama saya sampai saya melihat tag versi.
Aaron Bertrand
2
@NickChammas - Kinerja yang terdegradasi secara tidak linier dengan jumlah nilai klausa BTW . Saya mengirimkan item terhubung dengan repro memasukkan 1000 baris dengan 10 VARCHAR(800)kolom pada 2008 dengan waktu kompilasi 12,5 menit pada contoh dev 2008 saya karena ia melakukan banyak pekerjaan yang tidak perlu membandingkan nilai daripada hanya melanjutkan dengan memasukkannya (melakukan banyak lebih cepat ketika parameter dan tidak ada nilai untuk dilihat). Meskipun jauh lebih baik pada tahun 2012, pola non-linear masih ada & harus diperbaiki dalam versi setelahnya.
Martin Smith
9

Saya tidak yakin mengapa Anda keluar dari kesalahan memori, tetapi ada pendekatan yang lebih mudah.

Jika Anda dapat mengekspor data dari spreadsheet ke dalam format yang dibatasi (mis. Csv), Anda dapat menggunakan panduan impor data di SSMS untuk memasukkan data untuk Anda:

Tugas impor data SSMS.

datagod
sumber
Itu sangat membantu tetapi saya tidak memiliki akses ke database klien. Saya harus menyiapkan patch dan dataload dalam skrip
spaghetticowboy
0

Menggunakan beberapa SqlBulkCopy, buat tabel temp. Masukkan data baru ke tabel temp, kemudian gabungkan data di tabel temp ke yang sudah ada. Contoh menggunakan Metode C # SqlBulkCopy.WriteToServer (DataTable) . Semoga ini bisa membantu

Hung Vu
sumber
0

Ya kita bisa melakukan itu, saya mencoba dengan pendekatan BCP (Bulk Copy Program) untuk menghindari masalah OutOfMemory .

Catatan : Mencoba di SQL Server 2014.

Di BCP, pertama-tama kita perlu mengekspor data basis data Sumber ke file bcp (dalam folder direktori lokal) dan kemudian perlu mengimpor file bcp ke database tujuan.

masukkan deskripsi gambar di sini

Berikut adalah langkah-langkah kue berjalan:

catatan:

a) Pastikan tabel kosong ada di database Tujuan

b) Pastikan folder Temp ada di drive C.

  1. Buat file kelelawar bernama Export_Data.bat dengan perintah di bawah ini:

    bcp.exe [Source_DataBase_Name].[dbo].[TableName] OUT "C:\Temp\TableName.bcp" -S "Computer Name" -U "SQL Server UserName" -P "SQL Server Password" -n -q 

    jeda

  2. Jalankan file bat itu, sebagai akibatnya file bcp akan dihasilkan di folder Temp

  3. Kemudian buat file kelelawar lain bernama Import_Data.bat dengan perintah berikut:

    bcp.exe [Destination_DataBase_Name].[dbo].[TableName] IN "C:\Temp\TableName.bcp" -S "Computer Name" -U "SQL Server UserName" -P "SQL Server Password" -n -q 

    Jeda

Dan ini dia!

Kms
sumber
Mendapatkan kesalahan "Nama tabel yang valid diperlukan untuk opsi masuk, keluar, atau format." ketika mencoba mengekspor data.
Sen Jacob
1
Bisakah Anda menempelkan perintah yang Anda coba dengan semua nilai atribut. Harap ikuti contoh di bawah ini: bcp.exe ExportDB.dbo.AddressCountry OUT "C: \ Temp \ AddressCountry.bcp" -S "IN-L20054" -U "sa" -P "sa" -n -q Dalam [ExportDB -> Source DB, AddressCountry-> Table Hadir dalam Source DB, IN-L20054 -> Nama Mesin, "sa" adalah nama pengguna / pwd dari DB]
Kms
Saya tidak memilikinya sekarang. Saya akhirnya menggunakan fitur impor data di SSMS. Kemudian terhubung target DB (v14.0) ke sumber DB (v.15.0) menggunakan koneksi MS OLE DB dan itu cukup cepat untuk mengimpor multi-juta baris data. Terima kasih!
Sen Jacob