วิธีการเร่งความเร็วการลบอย่างมากจาก <table> โดยไม่มีส่วนคำสั่ง


37

ใช้ SQL Server 2005

ฉันกำลังทำการลบขนาดใหญ่จากที่ไม่มีส่วนคำสั่ง โดยทั่วไปแล้วเทียบเท่ากับคำสั่ง TRUNCATE TABLE - ยกเว้นฉันไม่ได้รับอนุญาตให้ใช้ TRUNCATE ปัญหาคือตารางมีขนาดใหญ่มาก - 10 ล้านแถวและใช้เวลากว่าหนึ่งชั่วโมงกว่าจะเสร็จสมบูรณ์ มีวิธีทำให้เร็วขึ้นโดยไม่:

  • ใช้ตัดทอน
  • ปิดการใช้งานหรือวางดัชนี?

t-log มีอยู่ในดิสก์ที่แยกต่างหาก

ข้อเสนอแนะใด ๆ ยินดีต้อนรับ!


2
หากคุณจะทำสิ่งนี้มากลองพิจารณาแบ่งตาราง
ออกุสตุส

1
คุณไม่สามารถใช้ TRUNCATE ได้เนื่องจากมีข้อ จำกัด FK อ้างอิงตาราง
Nick Chammas

คำตอบ:


39

สิ่งที่คุณสามารถทำได้คือการลบแบทช์เช่นนี้:

SELECT 'Starting' --sets @@ROWCOUNT
WHILE @@ROWCOUNT <> 0
    DELETE TOP (xxx) MyTable

ที่ xxx คือที่ไหนพูด 50000

การปรับเปลี่ยนนี้หากคุณต้องการลบเปอร์เซ็นต์ของแถวที่สูงมาก ...

SELECT col1, col2, ... INTO #Holdingtable
           FROM MyTable WHERE ..some condition..

SELECT 'Starting' --sets @@ROWCOUNT
WHILE @@ROWCOUNT <> 0
    DELETE TOP (xxx) MyTable WHERE ...

INSERT MyTable (col1, col2, ...)
           SELECT col1, col2, ... FROM #Holdingtable

3
@tuseau: การลบแต่ละรายการต้องใช้พื้นที่บันทึกบางส่วนในกรณีที่เกิดข้อผิดพลาดเพื่อย้อนกลับ การลบแถวขนาด 50k ใช้ทรัพยากร / พื้นที่น้อยกว่าการลบแถวขนาด 10 เมตร แน่นอนว่าการสำรองข้อมูลบันทึกยังคงทำงานอยู่และใช้พื้นที่ แต่มันง่ายกว่าสำหรับเซิร์ฟเวอร์ที่มีชุดข้อมูลขนาดเล็กจำนวนมาก
GBN

1
ขอขอบคุณการลบแบทช์ช่วยฉันคิดว่ามันเป็นตัวเลือกที่ดีที่สุด
tuseau

2
@Phil Helmer: หากการลบแบทช์อยู่ในการทำธุรกรรมจะไม่มีการใช้กำไร มิฉะนั้นแต่ละล็อกแบบเขียนมีขนาดเล็กซึ่งเป็นเพียงแค่โหลดง่าย
GBN

1
อีกหนึ่งความคิดเห็นเพิ่มเติม: การลบแบบกลุ่มช่วยอย่างมหาศาลและใช้เวลาลบ 20 ล้านแถวจาก 1 ชั่วโมง 42 นาทีถึง 3 นาที - แต่ให้แน่ใจว่าตารางนั้นมีดัชนีคลัสเตอร์! ถ้ามันเป็นฮีปประโยค TOP จะสร้างแผนการดำเนินการที่ไม่เห็นด้วยกับการปรับปรุงใด ๆ ดูเหมือนจะชัดเจนในภายหลัง
tuseau

2
@Noumenon: ทำให้มั่นใจได้ว่า @@ ROWCOUNT คือ 1
gbn

21

คุณสามารถใช้คำสั่ง TOP เพื่อทำสิ่งนี้ให้ง่ายขึ้น:

WHILE (1=1)
BEGIN
    DELETE TOP(1000) FROM table
    IF @@ROWCOUNT < 1 BREAK
END

วงเล็บปีกกาจัดรูปแบบโค้ดของคุณ
gbn

@gbn นั่นคือดังนั้น ที่นี่ยังคงเป็น 101 010
bernd_k

7

ฉันเห็นด้วยกับข้อเสนอแนะในการจัดกลุ่มการลบของคุณเป็นกลุ่มที่จัดการได้หากคุณไม่สามารถใช้ TRUNCATE และฉันชอบคำแนะนำแบบหล่น / สร้างสำหรับความคิดริเริ่ม แต่ฉันอยากรู้เกี่ยวกับความคิดเห็นต่อไปนี้ในคำถามของคุณ:

โดยทั่วไปแล้วเทียบเท่ากับคำสั่ง TRUNCATE TABLE - ยกเว้นฉันไม่ได้รับอนุญาตให้ใช้ TRUNCATE

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

สมมติว่าเป็นกรณีนี้ฉันสงสัยว่าถ้ามีขั้นตอนการจัดเก็บที่สร้างขึ้นโดยใช้ TRUNCATE TABLE และใช้ "EXECUTE AS" จะถือว่าเป็นทางเลือกที่มีประโยชน์ในการให้สิทธิ์การรักษาความปลอดภัยที่จำเป็นในการตัดตารางโดยตรง

หวังว่านี่จะให้ความเร็วที่คุณต้องการพร้อมกับจัดการปัญหาด้านความปลอดภัยที่ บริษัท ของคุณอาจมีเมื่อเพิ่มบัญชีของคุณไปยังบทบาท db_ddladmin

ข้อดีอีกประการของการใช้โพรซีเดอร์ที่เก็บด้วยวิธีนี้คือโพรซีเดอร์ที่เก็บนั้นอาจถูกล็อคเพื่อให้อนุญาตเฉพาะแอคเคาท์ที่เจาะจงเท่านั้น

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

หวังว่านี่จะช่วยได้!


5

ยกเว้นการตัด .. การลบเป็นชุดเท่านั้นสามารถช่วยคุณได้

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


เนื่องจากแอปพลิเคชันได้รับการออกแบบมาสำหรับการทำงานพร้อมกันการเปลี่ยนโครงสร้าง (DDL) และการใช้การตัดทอนไม่ใช่ตัวเลือก ... ฉันคิดว่าการลบชุดเป็นสิ่งที่ดีที่สุด ขอบคุณ
tuseau

1

เนื่องจากคำถามนี้เป็นข้อมูลอ้างอิงที่สำคัญฉันจึงโพสต์โค้ดนี้ซึ่งช่วยให้ฉันเข้าใจการลบลูปและส่งข้อความภายในวงเพื่อติดตามความคืบหน้า

แบบสอบถามที่มีการแก้ไขจากนี้คำถามที่ซ้ำกัน ให้เครดิต@RLFสำหรับฐานการสืบค้น

CREATE TABLE #DelTest (ID INT IDENTITY, name NVARCHAR(128)); -- Build the test table
INSERT INTO #DelTest (name) SELECT name FROM sys.objects;  -- fill from system DB
SELECT COUNT(*) TableNamesContainingSys FROM #deltest WHERE name LIKE '%sys%'; -- check rowcount
go
DECLARE @HowMany INT;
DECLARE @RowsTouched INT;
DECLARE @TotalRowCount INT;
DECLARE @msg VARCHAR(100);
DECLARE @starttime DATETIME 
DECLARE @currenttime DATETIME 

SET @RowsTouched = 1; -- Needs to be >0 for loop to start
SET @TotalRowCount=0  -- Total rows deleted so far is 0
SET @HowMany = 5;     -- Variable to choose how many rows to delete per loop
SET @starttime=GETDATE()

WHILE @RowsTouched > 0
BEGIN
   DELETE TOP (@HowMany)
   FROM #DelTest 
   WHERE name LIKE '%sys%';

   SET @RowsTouched = @@ROWCOUNT; -- Rows deleted this loop
   SET @TotalRowCount = @TotalRowCount+@RowsTouched; -- Increment Total rows deleted count
   SET @currenttime = GETDATE();
   SELECT @msg='Deleted ' + CONVERT(VARCHAR(9),@TotalRowCount) + ' Records. Runtime so far is '+CONVERT(VARCHAR(30),DATEDIFF(MILLISECOND,@starttime,@currenttime))+' milliseconds.'
   RAISERROR(@msg, 0, 1) WITH NOWAIT;  -- Print message after every loop. Can't use the PRINT function as SQL buffers output in loops.  

END; 
SELECT COUNT(*) TableNamesContainingSys FROM #DelTest WHERE name LIKE '%sys%'; -- Check row count after loop finish
DROP TABLE #DelTest;
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.