ฉันมีตารางข้อมูลขนาดใหญ่ มีข้อมูล 10 ล้านรายการในตารางนี้
วิธีที่ดีที่สุดสำหรับแบบสอบถามนี้คืออะไร
Delete LargeTable where readTime < dateadd(MONTH,-7,GETDATE())
ฉันมีตารางข้อมูลขนาดใหญ่ มีข้อมูล 10 ล้านรายการในตารางนี้
วิธีที่ดีที่สุดสำหรับแบบสอบถามนี้คืออะไร
Delete LargeTable where readTime < dateadd(MONTH,-7,GETDATE())
คำตอบ:
หากคุณกำลังลบแถวทั้งหมดในตารางนั้นตัวเลือกที่ง่ายที่สุดคือ Truncate table เช่น
TRUNCATE TABLE LargeTable
GO
Truncate table จะทำให้ตารางว่างเปล่าคุณไม่สามารถใช้ WHERE clause เพื่อ จำกัด แถวที่ถูกลบและจะไม่มีการเรียกใช้ทริกเกอร์
ในทางกลับกันหากคุณกำลังลบข้อมูลมากกว่า 80-90 เปอร์เซ็นต์ให้บอกว่าคุณมีทั้งหมด 11 ล้านแถวและคุณต้องการลบ 10 ล้านอีกวิธีหนึ่งคือการแทรก 1 ล้านแถวเหล่านี้ (บันทึกที่คุณต้องการเก็บไว้ ) ไปยังตารางการแสดงละครอื่น ตัดทอนตารางขนาดใหญ่นี้และแทรกกลับ 1 ล้านแถวเหล่านี้
หรือถ้าสิทธิ์ / มุมมองหรือวัตถุอื่น ๆ ที่มีตารางขนาดใหญ่นี้เป็นตารางพื้นฐานไม่ได้รับผลกระทบจากการวางตารางนี้คุณจะได้รับจำนวนแถวที่ค่อนข้างเล็กเหล่านี้ลงในตารางอื่นให้วางตารางนี้และสร้างตารางอื่นด้วยสคีมาเดียวกันและนำเข้าสิ่งเหล่านี้ แถวกลับเข้าไปในตารางอดีตขนาดใหญ่นี้
ตัวเลือกสุดท้ายที่ฉันคิดได้คือเปลี่ยนฐานข้อมูลของคุณRecovery Mode to SIMPLE
แล้วลบแถวเป็นกลุ่มเล็ก ๆ โดยใช้ while loop อะไรทำนองนี้ ..
DECLARE @Deleted_Rows INT;
SET @Deleted_Rows = 1;
WHILE (@Deleted_Rows > 0)
BEGIN
-- Delete some small number of rows at a time
DELETE TOP (10000) LargeTable
WHERE readTime < dateadd(MONTH,-7,GETDATE())
SET @Deleted_Rows = @@ROWCOUNT;
END
และอย่าลืมเปลี่ยนโหมดการกู้คืนกลับเป็นโหมดเต็มและฉันคิดว่าคุณต้องสำรองข้อมูลเพื่อให้ได้ผลเต็มที่ (โหมดการเปลี่ยนแปลงหรือการกู้คืน)
optimal solution for unknown case
นั่นคือความฝันไม่ใช่เหรอ? น่าเสียดายที่คุณไม่สามารถรักษาทุกโรคได้ด้วยยาเม็ดเดียว ฉันได้แนะนำวิธีแก้ปัญหาที่เป็นไปได้สำหรับสถานการณ์ต่างๆ ไม่มีกระสุนเศษไม้ที่นี่น่าเสียดาย
คำตอบของ @ m-ali นั้นถูกต้อง แต่โปรดทราบว่าบันทึกอาจเพิ่มขึ้นได้มากหากคุณไม่ทำธุรกรรมหลังจากแต่ละชิ้นและทำการตรวจสอบ นี่คือวิธีที่ฉันจะทำและใช้บทความนี้http://sqlperformance.com/2013/03/io-subsystem/chunk-deletesเป็นข้อมูลอ้างอิงพร้อมการทดสอบประสิทธิภาพและกราฟ:
DECLARE @Deleted_Rows INT;
SET @Deleted_Rows = 1;
WHILE (@Deleted_Rows > 0)
BEGIN
BEGIN TRANSACTION
-- Delete some small number of rows at a time
DELETE TOP (10000) LargeTable
WHERE readTime < dateadd(MONTH,-7,GETDATE())
SET @Deleted_Rows = @@ROWCOUNT;
COMMIT TRANSACTION
CHECKPOINT -- for simple recovery model
END
COMMIT TRANSACTION
และCHECKPOINT
ท่อนไม้ยังคงเติบโต ขอบคุณที่แจ้งเรื่องนี้ให้ชัดเจน
@Deleted_Rows
กับ 10,000 หรือคุณอาจจบลงด้วยการวนซ้ำไม่สิ้นสุดเนื่องจากการลบข้อมูลชุดเล็ก ๆ ไปเรื่อย ๆ ดังนั้นWHILE (@Deleted_Rows = 10000)
- ทันทีที่ไม่มี "หน้า" เต็มของข้อมูลที่จะลบมันจะหยุดลง ในการนำไปใช้งานของคุณWHILE (@Deleted_Rows > 0)
while-loop จะดำเนินการอีกครั้งแม้ว่าจะลบเพียงแถวเดียวก็ตามและการดำเนินการถัดไปอาจพบแถวหรือสองแถวที่จะลบซึ่งส่งผลให้เกิดการวนซ้ำแบบไม่สิ้นสุด
WHILE
dateadd(MONTH,-7,GETDATE())
WHILE
วง
คุณยังสามารถใช้ GO + กี่ครั้งที่คุณต้องการดำเนินการสืบค้นเดียวกัน
DELETE TOP (10000) [TARGETDATABASE].[SCHEMA].[TARGETTABLE]
WHERE readTime < dateadd(MONTH,-1,GETDATE());
-- how many times you want the query to repeat
GO 100
GO xx
ควรจะทำงานจากอะไร ฉันได้รับข้อผิดพลาด"ไม่พบขั้นตอนการจัดเก็บ" " หากไม่มีGO
คำสั่งมันก็ทำงานได้ดี
@ ฟรานซิสโกโกลเดนสไตน์แก้ไขเพียงเล็กน้อย ต้องใช้ COMMIT หลังจากที่คุณตั้งค่าตัวแปรมิฉะนั้น WHILE จะถูกดำเนินการเพียงครั้งเดียว:
DECLARE @Deleted_Rows INT;
SET @Deleted_Rows = 1;
WHILE (@Deleted_Rows > 0)
BEGIN
BEGIN TRANSACTION
-- Delete some small number of rows at a time
DELETE TOP (10000) LargeTable
WHERE readTime < dateadd(MONTH,-7,GETDATE())
SET @Deleted_Rows = @@ROWCOUNT;
COMMIT TRANSACTION
CHECKPOINT -- for simple recovery model
END
รูปแบบนี้ของM.Aliทำงานได้ดีสำหรับฉัน มันลบบางส่วนล้างบันทึกและทำซ้ำ ฉันเฝ้าดูบันทึกที่เติบโตปล่อยวางและเริ่มต้นใหม่
DECLARE @Deleted_Rows INT;
SET @Deleted_Rows = 1;
WHILE (@Deleted_Rows > 0)
BEGIN
-- Delete some small number of rows at a time
delete top (100000) from InstallLog where DateTime between '2014-12-01' and '2015-02-01'
SET @Deleted_Rows = @@ROWCOUNT;
dbcc shrinkfile (MobiControlDB_log,0,truncateonly);
END
# of rows
เพื่อลบในแต่ละครั้งและWHERE
ประโยคด้วย ใช้งานได้เหมือนมีเสน่ห์!
หากคุณเต็มใจ (และสามารถ) ที่จะใช้การแบ่งพาร์ติชันนั่นเป็นเทคนิคที่มีประสิทธิภาพในการลบข้อมูลจำนวนมากโดยมีค่าใช้จ่ายในการรันไทม์เพียงเล็กน้อย ไม่คุ้มค่าสำหรับการออกกำลังกายครั้งเดียว
ผมสามารถที่จะลบ 19 ล้านแถวจากโต๊ะของเรา 21 ล้านแถวในไม่กี่นาที นี่คือแนวทางของฉัน
หากคุณมีคีย์หลักที่เพิ่มขึ้นโดยอัตโนมัติในตารางนี้คุณสามารถใช้คีย์หลักนี้ได้
รับค่าต่ำสุดของคีย์หลักของตารางขนาดใหญ่ที่ readTime <dateadd (MONTH, -7, GETDATE ()) (เพิ่มดัชนีใน readTime หากยังไม่มีอยู่ดัชนีนี้จะถูกลบไปพร้อมกับตารางในขั้นตอนที่ 3) ให้เก็บไว้ในตัวแปร 'min_primary'
แทรกแถวทั้งหมดที่มีคีย์หลัก> min_primary ลงในตารางการจัดเตรียม (ตารางหน่วยความจำหากไม่มีแถวไม่ใหญ่)
วางโต๊ะขนาดใหญ่
สร้างตารางใหม่ คัดลอกแถวทั้งหมดจากตารางการแสดงละครไปยังตารางหลัก
วางตารางการแสดงละคร
คุณสามารถลบแบทช์ขนาดเล็กโดยใช้ while loop โดยมีลักษณะดังนี้:
DELETE TOP (10000) LargeTable
WHERE readTime < dateadd(MONTH,-7,GETDATE())
WHILE @@ROWCOUNT > 0
BEGIN
DELETE TOP (10000) LargeTable
WHERE readTime < dateadd(MONTH,-7,GETDATE())
END
การใช้งานอื่น:
SET ROWCOUNT 1000 -- Buffer
DECLARE @DATE AS DATETIME = dateadd(MONTH,-7,GETDATE())
DELETE LargeTable WHERE readTime < @DATE
WHILE @@ROWCOUNT > 0
BEGIN
DELETE LargeTable WHERE readTime < @DATE
END
SET ROWCOUNT 0
ไม่จำเป็น;
หากเปิดใช้งานบันทึกธุรกรรมให้ปิดใช้งานบันทึกธุรกรรม
ALTER DATABASE dbname SET RECOVERY SIMPLE;
ไวยากรณ์ที่สั้นกว่า
select 1
WHILE (@@ROWCOUNT > 0)
BEGIN
DELETE TOP (10000) LargeTable
WHERE readTime < dateadd(MONTH,-7,GETDATE())
END
หากคุณใช้ SQL server 2016 หรือสูงกว่าและหากตารางของคุณมีพาร์ติชันที่สร้างขึ้นตามคอลัมน์ที่คุณพยายามลบ (เช่นคอลัมน์ Timestamp) คุณสามารถใช้คำสั่งใหม่นี้เพื่อลบข้อมูลตามพาร์ติชัน
ตัดทอนตารางด้วย (PARTITIONS ({|} [, ... n]))
การดำเนินการนี้จะลบข้อมูลในพาร์ติชันที่เลือกเท่านั้นและควรเป็นวิธีที่มีประสิทธิภาพที่สุดในการลบข้อมูลจากส่วนหนึ่งของตารางเนื่องจากจะไม่สร้างบันทึกธุรกรรมและจะทำได้เร็วเท่ากับการตัดทอนปกติ แต่จะไม่มีการลบข้อมูลทั้งหมด จากโต๊ะ
ข้อเสียเปรียบคือถ้าตารางของคุณไม่ได้ตั้งค่าด้วยพาร์ติชันคุณต้องไปโรงเรียนเก่าและลบข้อมูลด้วยวิธีการปกติจากนั้นสร้างตารางใหม่ด้วยพาร์ติชันเพื่อให้คุณสามารถทำสิ่งนี้ได้ในอนาคตซึ่งเป็นสิ่งที่ฉันทำ ฉันเพิ่มการสร้างและลบพาร์ติชันลงในขั้นตอนการแทรกเอง ฉันมีตารางที่มี 500 ล้านแถวดังนั้นนี่จึงเป็นตัวเลือกเดียวที่จะลดเวลาในการลบ
สำหรับรายละเอียดเพิ่มเติมโปรดดูลิงค์ด้านล่าง: https://docs.microsoft.com/en-us/sql/t-sql/statements/truncate-table-transact-sql?view=sql-server-2017
ตารางที่ตัดทอน 2016 เซิร์ฟเวอร์ SQL พร้อมพาร์ติชัน
ด้านล่างนี้คือสิ่งที่ฉันทำก่อนเพื่อลบข้อมูลก่อนที่ฉันจะสร้างตารางใหม่โดยมีพาร์ติชันที่มีข้อมูลที่ต้องการ แบบสอบถามนี้จะทำงานเป็นเวลาหลายวันในช่วงเวลาที่กำหนดจนกว่าข้อมูลจะถูกลบ
:connect <<ServerName>>
use <<DatabaseName>>
SET NOCOUNT ON;
DECLARE @Deleted_Rows INT;
DECLARE @loopnum INT;
DECLARE @msg varchar(100);
DECLARE @FlagDate datetime;
SET @FlagDate = getdate() - 31;
SET @Deleted_Rows = 1;
SET @loopnum = 1;
/*while (getdate() < convert(datetime,'2018-11-08 14:00:00.000',120))
BEGIN
RAISERROR( 'WAIT for START' ,0,1) WITH NOWAIT
WAITFOR DELAY '00:10:00'
END*/
RAISERROR( 'STARTING PURGE' ,0,1) WITH NOWAIT
WHILE (1=1)
BEGIN
WHILE (@Deleted_Rows > 0 AND (datepart(hh, getdate() ) >= 12 AND datepart(hh, getdate() ) <= 20)) -- (getdate() < convert(datetime,'2018-11-08 19:00:00.000',120) )
BEGIN
-- Delete some small number of rows at a time
DELETE TOP (500000) dbo.<<table_name>>
WHERE timestamp_column < convert(datetime, @FlagDate,102)
SET @Deleted_Rows = @@ROWCOUNT;
WAITFOR DELAY '00:00:01'
select @msg = 'ROWCOUNT' + convert(varchar,@Deleted_Rows);
set @loopnum = @loopnum + 1
if @loopnum > 1000
begin
begin try
DBCC SHRINKFILE (N'<<databasename>>_log' , 0, TRUNCATEONLY)
RAISERROR( @msg ,0,1) WITH NOWAIT
end try
begin catch
RAISERROR( 'DBCC SHRINK' ,0,1) WITH NOWAIT
end catch
set @loopnum = 1
end
END
WAITFOR DELAY '00:10:00'
END
select getdate()
ถ้าฉันบอกว่าไม่มีลูปฉันสามารถใช้GOTO
คำสั่งเพื่อลบบันทึกจำนวนมากโดยใช้เซิร์ฟเวอร์ sql EXA
IsRepeat:
DELETE TOP (10000)
FROM <TableName>
IF @@ROWCOUNT > 0
GOTO IsRepeat
ด้วยวิธีนี้คุณสามารถลบข้อมูลจำนวนมากโดยมีขนาดการลบน้อยลง
โปรดแจ้งให้เราทราบหากต้องการข้อมูลเพิ่มเติม