ฉันจะรันสคริปต์ขนาดใหญ่ที่มีส่วนแทรกจำนวนมากโดยไม่ใช้หน่วยความจำไม่เพียงพอได้อย่างไร


28

คำถาม:

ฉันมีสคริปต์ที่มีการแทรกประมาณ 45,000 รายการจากคำสั่งที่เลือก เมื่อฉันลองและเรียกใช้ฉันได้รับข้อความแสดงข้อผิดพลาดระบุว่าฉันมีหน่วยความจำไม่เพียงพอ ฉันจะทำให้สคริปต์นี้ทำงานได้อย่างไร

บริบท:

  1. เพิ่มเขตข้อมูลใหม่บางส่วนเพื่อให้แอปพลิเคชันเล่นได้ดีกับแอปอื่นที่ลูกค้าใช้
  2. มีสเปรดชีตข้อมูลจากลูกค้าที่เต็มไปด้วยข้อมูลที่แมปไอเท็มข้อมูลปัจจุบันกับค่าสำหรับฟิลด์ใหม่เหล่านี้
  3. แปลงสเปรดชีตเพื่อแทรกคำสั่ง
  4. ถ้าฉันใช้คำสั่งบางคำสั่งใช้งานได้ แต่สคริปต์ทั้งหมดไม่ทำงาน
  5. ไม่ไม่มีการพิมพ์ผิด

หากมีวิธีอื่นที่ฉันควรจะโหลดข้อมูลนี้เพื่อลงโทษฉันและแจ้งให้เราทราบ


คำถามที่คล้ายกันเกี่ยวกับ SO: ( stackoverflow.com/questions/222442/ … ) ไม่แน่ใจว่าคำตอบนั้นช่วยได้หรือไม่
jumpdart

คำตอบ:


17

ขนาดแบตช์สูงสุดสำหรับ SQL Server 2005 คือ 65,536 * ขนาดแพ็คเก็ตเครือข่าย (NPS) โดยที่ NPS มักเป็น 4KB ใช้งานได้ถึง 256 MB นั่นหมายความว่าใบแจ้งยอดของคุณจะมีค่าเฉลี่ย 5.8 KB ดูเหมือนจะไม่ถูกต้อง แต่อาจมีช่องว่างที่ไม่เกี่ยวข้องหรือสิ่งผิดปกติในนั้น

คำแนะนำแรกของฉันคือการใส่คำสั่ง "GO" หลังจากทุกคำสั่ง INSERT สิ่งนี้จะแยกชุดคำสั่ง INSERT 45,000 ชุดของคุณออกเป็นชุดละ 45,000 ชุด ควรย่อยง่ายกว่า ระวังถ้ามีเม็ดมีดอันใดอันหนึ่งผิดพลาดคุณอาจพบว่ามีผู้ร้ายได้ยาก คุณอาจต้องการป้องกันตัวเองด้วยการทำธุรกรรม คุณสามารถเพิ่มข้อความเหล่านั้นได้อย่างรวดเร็วหากเครื่องมือแก้ไขของคุณมีการค้นหาและแทนที่ที่ดี (ซึ่งจะช่วยให้คุณค้นหาและแทนที่อักขระที่ส่งคืนเช่น \ r \ n) หรือสิ่งอำนวยความสะดวกของแมโคร

ข้อเสนอแนะที่สองคือการใช้ตัวช่วยสร้างเพื่อนำเข้าข้อมูลโดยตรงจาก Excel ตัวช่วยสร้างจะสร้างแพ็คเกจ SSIS เล็ก ๆ สำหรับคุณที่อยู่เบื้องหลังแล้วเรียกใช้สิ่งนั้น มันจะไม่มีปัญหานี้


2
GOหลังจากทุกคำสั่ง? ฉันเดาว่าคุณกำลังสร้างพวกเขาโดยใช้สคริปต์อื่นที่ไม่เป็นไร มิฉะนั้นฉันจะใส่หนึ่งครั้งหลังจากทุก ๆ 1,000 INSERTวินาที สำหรับการทำทรานแซกชันของอะตอมและการลดขนาดของทรานแซคชั่นทำไมไม่โหลดแถวทั้งหมดลงในตารางชั่วคราวหรือตัวแปรตารางแล้วโหลดมันในภาพเดียวจากที่นั่นไปยังตารางเป้าหมาย
Nick Chammas

1,000 นั้นดีเท่ากับ 1 แต่ยากที่จะนับ พูดตามตรงเขาอาจหนีด้วยประโยค GO เพียงอันเดียวที่เครื่องหมายกึ่งกลางใกล้กับข้อความ 21,500 ฉันชอบ GO fix เพราะไม่ต้องแก้ไขสคริปต์ปัจจุบันหรือนับคำสั่ง INSERT ที่ซับซ้อน (ซึ่งอาจไม่จับคู่กับหมายเลขบรรทัดโดยตรง)
ช่องแคบดารินทร์

2
แน่นอนว่าแม้แต่การประมาณ 1,000 ข้อความที่ไม่ดีก็เพียงพอแล้ว :)
Nick Chammas

1
การเพิ่ม GOs เป็นการแก้ไขที่ง่ายและรวดเร็ว .. สคริปต์ 25mb ทำงานในเวลาน้อยกว่า 9 นาทีโดยไม่มีปัญหา ต้องการให้มันเป็นสคริปต์เพื่อเก็บไว้ในกระบวนการปรับใช้แพตช์มาตรฐานของเราเมื่อมันดับ
spaghetticowboy

14

BULK INSERTหรือbcpดูตัวเลือกที่เหมาะสมกว่า 45,000 คำสั่งแทรก

หากคุณต้องการติดกับคำสั่งแทรกฉันจะพิจารณาตัวเลือกไม่กี่:

ตอบ: ใช้ธุรกรรมและห่อแบทช์ของงบ 100 หรือ 500 หรือ 1,000 รายการในแต่ละรายการเพื่อลดผลกระทบต่อบันทึกและแบทช์ เช่น

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: แทนที่จะใช้คำสั่งแทรกแต่ละคำสั่งใช้คำสั่งUNION ALL100 หรือ 500 คำสั่งในแต่ละครั้งเช่น

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

ฉันเหลือข้อผิดพลาดในการจัดการกับความกะทัดรัด แต่ประเด็นก็คือฉันจะไม่พยายามส่งชุดคำสั่งเดี่ยวจำนวน 45,000 ชุดไปยัง SQL Server


1
เสียดายที่ OP ไม่สามารถใช้ตัวสร้างตารางค่าคุณลักษณะ 2008+ เขายังคงต้องแบทช์แทรกเป็นกลุ่ม 1,000 แถวซึ่งเป็นจำนวนสูงสุดที่คุณสามารถจัดกลุ่มพร้อมกับ TVC
Nick Chammas

นั่นจะเป็นคำแนะนำแรกของฉันจนกว่าฉันจะเห็นแท็กเวอร์ชัน
Aaron Bertrand

2
@NickChammas - ประสิทธิภาพการทำงานของ degrades ผู้ที่ไม่เป็นเส้นตรงกับจำนวนของค่าข้อ BTW ฉันส่งรายการเชื่อมต่อที่มีการแทรกจำนวน 1,000 แถวที่มี 10 VARCHAR(800)คอลัมน์ในปี 2008 ด้วยเวลารวบรวม 12.5 นาทีในอินสแตนซ์ dev 2008 ของฉันเนื่องจากทำงานที่ไม่จำเป็นจำนวนมากเปรียบเทียบค่ามากกว่าการเริ่มต้นด้วยการแทรกพวกเขา เร็วขึ้นเมื่อทำการกำหนดพารามิเตอร์และไม่มีค่าให้ดู) แม้ว่าการปรับปรุงจะดีขึ้นมากในปี 2012 แต่รูปแบบที่ไม่ใช่เชิงเส้นยังคงมีอยู่ & ควรได้รับการแก้ไขในรุ่นหลังจาก
Martin Smith

9

ฉันไม่แน่ใจว่าทำไมคุณได้รับข้อผิดพลาดหน่วยความจำไม่เพียงพอ แต่มีวิธีการที่ง่ายกว่า

หากคุณสามารถส่งออกข้อมูลจากสเปรดชีตเป็นรูปแบบที่มีตัวคั่น (เช่น csv) คุณสามารถใช้ตัวช่วยสร้างการนำเข้าข้อมูลใน SSMS เพื่อแทรกข้อมูลให้คุณ:

งานนำเข้าข้อมูล SSMS


thats เป็นประโยชน์ แต่ฉันไม่สามารถเข้าถึงฐานข้อมูลลูกค้า ฉันต้องเตรียมแพทช์และดาต้าล็อกในสคริปต์
spaghetticowboy

0

ใช้ SqlBulkCopy หลายอันสร้างตารางชั่วคราว แทรกข้อมูลใหม่ลงในตาราง temp จากนั้นรวมข้อมูลในตาราง temp ลงในข้อมูลที่มีอยู่ ตัวอย่างการใช้ C # วิธี SqlBulkCopy.WriteToServer (DataTable) หวังว่ามันจะช่วย


0

ใช่เราทำได้ฉันลองใช้วิธีBCP (โปรแกรมคัดลอกขนาดใหญ่) เพื่อหลีกเลี่ยงปัญหาOutOfMemory

หมายเหตุ : พยายามบน SQL Server 2014

ใน BCP อันดับแรกเราต้องส่งออกข้อมูลฐานข้อมูลต้นทางไปยังไฟล์bcp (ในโฟลเดอร์ไดเรกทอรีในเครื่อง) จากนั้นจำเป็นต้องนำเข้าไฟล์bcpนั้นไปยังฐานข้อมูลปลายทาง

ป้อนคำอธิบายรูปภาพที่นี่

ด้านล่างเป็นขั้นตอนการเดินเค้ก:

บันทึก:

a) ตรวจสอบให้แน่ใจว่ามีตารางว่างอยู่ในฐานข้อมูลปลายทาง

b) ตรวจสอบให้แน่ใจว่าโฟลเดอร์ชั่วคราวมีอยู่ในไดรฟ์C

  1. สร้างไฟล์ค้างคาวชื่อเป็นExport_Data.batด้วยคำสั่งที่แสดงด้านล่าง:

    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 

    หยุด

  2. เรียกใช้ไฟล์ bat นั้นเนื่องจากไฟล์bcpจะถูกสร้างขึ้นในโฟลเดอร์Temp

  3. จากนั้นสร้างไฟล์ bat อีกไฟล์ชื่อเป็นImport_Data.batด้วยคำสั่งต่อไปนี้:

    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 

    หยุด

และไปเลย!


การรับข้อผิดพลาด "ต้องใช้ชื่อตารางที่ถูกต้องสำหรับตัวเลือกเข้าหรือออก เมื่อพยายามส่งออกข้อมูล
Sen Jacob

1
คุณสามารถวางคำสั่งที่คุณลองด้วยค่าแอตทริบิวต์ทั้งหมดได้โปรดทำตามตัวอย่างด้านล่าง: bcp.exe ExportDB.dbo.AddressCountry OUT "C: \ Temp \ AddressCountry.bcp" -S "IN-L20054" -U "sa" -P "sa" -n -q ใน [ExportDB -> DB ต้นทาง, AddressCountry-> ตารางนำเสนอใน Source DB, IN-L20054 -> ชื่อเครื่อง "sa" เป็นชื่อผู้ใช้ / pwd ของ DB]
Kms

ฉันไม่มีตอนนี้ ฉันลงเอยด้วยการใช้คุณสมบัตินำเข้าข้อมูลใน SSMS จากนั้นเชื่อมต่อ DB เป้าหมาย (v14.0) กับ DB ต้นทาง (v.15.0) โดยใช้การเชื่อมต่อ MS OLE DB และมันค่อนข้างเร็วในการนำเข้าข้อมูลหลายล้านแถว ขอบคุณ!
Sen Jacob
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.