Các phương pháp tăng tốc độ XÓA lớn TỪ <bảng> không có mệnh đề


37

Sử dụng SQL Server 2005.

Tôi đang thực hiện một XÓA lớn TỪ không có mệnh đề. Về cơ bản, nó tương đương với câu lệnh TRUNCATE TABLE - ngoại trừ tôi không được phép sử dụng TRUNCATE. Vấn đề là cái bàn rất lớn - 10 triệu hàng và phải mất hơn một giờ để hoàn thành. Có cách nào làm cho nó nhanh hơn mà không cần:

  • Sử dụng cắt ngắn
  • Vô hiệu hóa hoặc giảm chỉ số?

Nhật ký t đã có trên một đĩa riêng.

Mọi góp ý đều được chào đón!


2
Nếu bạn sẽ làm điều này rất nhiều, hãy xem xét phân vùng bảng
Gaius

1
Bạn không thể sử dụng TRUNCATE vì có các ràng buộc FK tham chiếu bảng?
Nick Chammas

Câu trả lời:


39

Những gì bạn có thể làm là xóa hàng loạt như thế này:

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

Xxx ở đâu

Một sửa đổi này, nếu bạn muốn loại bỏ một tỷ lệ rất cao của hàng ...

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: mỗi lần xóa yêu cầu một số không gian nhật ký trong trường hợp có lỗi, để khôi phục. Xóa hàng 50k mất ít tài nguyên / không gian hơn xóa hàng 10m. Tất nhiên, các bản sao lưu nhật ký vẫn chạy, v.v.
gbn

1
Cảm ơn, xóa hàng loạt giúp một chút, tôi đoán đó là lựa chọn tốt nhất.
tuseau

2
@Phil Helmer: nếu xóa hàng loạt trong một giao dịch thì không có lợi ích nào được sử dụng. Mặt khác, mỗi lần ghi nhật ký nhỏ hơn, đơn giản là, tải dễ dàng hơn
gbn

1
Thêm một nhận xét: việc xóa hàng loạt giúp rất nhiều và xóa 20 triệu hàng xuống từ 1 giờ 42 phút xuống còn 3 phút - NHƯNG hãy chắc chắn rằng bảng có một chỉ mục được nhóm! Nếu đó là một đống, mệnh đề TOP tạo ra một loại trong kế hoạch thực hiện, phủ nhận mọi cải tiến. Có vẻ rõ ràng sau đó.
tuseau

2
@Noumenon: Nó đảm bảo @@ ROWCOUNT là 1
gbn

21

Bạn có thể sử dụng mệnh đề TOP để thực hiện điều này một cách dễ dàng:

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

Dấu ngoặc nhọn định dạng mã của bạn
gbn

@gbn Đó là trên SO. đây vẫn là 101 010.
bernd_k

7

Tôi đồng ý với các đề xuất để xóa các lần xóa của bạn thành các phần có thể quản lý nếu bạn không thể sử dụng TRUNCATE và tôi thích đề xuất thả / tạo cho tính nguyên bản của nó, nhưng tôi tò mò về nhận xét sau trong câu hỏi của bạn:

Về cơ bản, nó tương đương với câu lệnh TRUNCATE TABLE - ngoại trừ tôi không được phép sử dụng TRUNCATE

Tôi đoán lý do cho sự hạn chế này có liên quan đến bảo mật cần được cấp để cắt trực tiếp một bảng và thực tế là nó sẽ cho phép bạn cắt bớt các bảng khác với bảng bạn quan tâm.

Giả sử là như vậy, tôi tự hỏi nếu có một thủ tục được lưu trữ được tạo bằng TRUNCATE TABLE và sử dụng "EXECUTE AS" sẽ được coi là một giải pháp thay thế khả thi để cung cấp các quyền bảo mật cần thiết để cắt trực tiếp bảng.

Hy vọng, điều này sẽ cung cấp cho bạn tốc độ bạn cần đồng thời giải quyết các mối lo ngại về bảo mật mà công ty bạn có thể có khi thêm tài khoản của bạn vào vai trò db_ddladmin.

Một ưu điểm khác của việc sử dụng một thủ tục được lưu trữ theo cách này là chính thủ tục được lưu trữ có thể bị khóa để chỉ những tài khoản cụ thể mới được phép sử dụng nó.

Nếu vì một lý do nào đó, đây không phải là một giải pháp có thể chấp nhận được và việc bạn cần xóa dữ liệu trong bảng này là việc cần thực hiện mỗi ngày một lần / giờ / v.v., tôi sẽ yêu cầu một công việc Tác nhân SQL được tạo ra để cắt bớt bảng vào một thời gian dự kiến ​​mỗi ngày.

Hi vọng điêu nay co ich!


5

Ngoại trừ cắt ngắn .. chỉ xóa trong lô có thể giúp bạn.

Bạn có thể bỏ bảng và tạo lại nó, với tất cả các ràng buộc và chỉ mục, tất nhiên. Trong Management Studio, bạn có tùy chọn để kịch bản một bảng để thả và tạo, vì vậy nó sẽ là một tùy chọn tầm thường. Nhưng điều này chỉ khi bạn được phép thực hiện các hành động DDL, mà tôi thấy đó không thực sự là một lựa chọn.


Bởi vì ứng dụng được thiết kế cho các hoạt động đồng thời, thay đổi cấu trúc (DDL) và sử dụng cắt ngắn không phải là tùy chọn ... Tôi đoán xóa hàng loạt là tốt nhất có sẵn. Cảm ơn mặc dù.
tuseau

1

Vì câu hỏi này là một tài liệu tham khảo quan trọng nên tôi đang đăng mã này thực sự giúp tôi hiểu việc xóa bằng các vòng lặp và cũng nhắn tin trong một vòng lặp để theo dõi tiến trình.

Các truy vấn được sửa đổi từ câu hỏi trùng lặp này . Tín dụng cho @RLF cho cơ sở truy vấn.

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;
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.