Tôi có một bảng dữ liệu lớn. Có 10 triệu hồ sơ trong bảng này.
Cách tốt nhất cho truy vấn này là gì
Delete LargeTable where readTime < dateadd(MONTH,-7,GETDATE())
Tôi có một bảng dữ liệu lớn. Có 10 triệu hồ sơ trong bảng này.
Cách tốt nhất cho truy vấn này là gì
Delete LargeTable where readTime < dateadd(MONTH,-7,GETDATE())
Câu trả lời:
Nếu bạn đang xóa Tất cả các hàng trong bảng đó, tùy chọn đơn giản nhất là cắt ngắn bảng, đại loại như
TRUNCATE TABLE LargeTable
GO
Bảng rút gọn sẽ đơn giản làm trống bảng, bạn không thể sử dụng mệnh đề WHERE để giới hạn các hàng bị xóa và sẽ không có kích hoạt nào được kích hoạt.
Mặt khác, nếu bạn đang xóa hơn 80-90 Phần trăm dữ liệu, hãy nói rằng nếu bạn có tổng số 11 triệu hàng và bạn muốn xóa 10 triệu cách khác sẽ là Chèn 1 triệu hàng này (các bản ghi bạn muốn giữ ) đến một bàn dàn khác. Cắt bớt bảng lớn này và chèn lại 1 triệu hàng này.
Hoặc nếu các quyền / khung nhìn hoặc các đối tượng khác có bảng lớn này vì bảng bên dưới của chúng không bị ảnh hưởng bằng cách bỏ bảng này, bạn có thể lấy một lượng tương đối nhỏ các hàng này vào một bảng khác thả bảng này và tạo một bảng khác có cùng lược đồ và nhập các bảng này hàng trở lại vào bảng cũ này.
Một tùy chọn cuối cùng tôi có thể nghĩ đến là thay đổi cơ sở dữ liệu của bạn Recovery Mode to SIMPLE
và sau đó xóa các hàng trong các lô nhỏ hơn bằng cách sử dụng vòng lặp while như thế này ..
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
và đừng quên thay đổi chế độ Recovery trở lại đầy đủ và tôi nghĩ rằng bạn phải sao lưu để làm cho nó hoàn toàn ảnh hưởng (chế độ thay đổi hoặc khôi phục).
optimal solution for unknown case
đó là giấc mơ phải không? Thật không may, bạn không thể chữa mọi bệnh bằng bất kỳ một viên thuốc nào; Tôi đã đề xuất một số giải pháp có thể cho các kịch bản khác nhau. Không có viên đạn cúi ở đây không may.
Câu trả lời @ m-ali là đúng nhưng cũng nên nhớ rằng nhật ký có thể phát triển rất nhiều nếu bạn không thực hiện giao dịch sau mỗi đoạn và thực hiện một điểm kiểm tra. Đây là cách tôi sẽ làm và lấy bài viết này http://sqlperformance.com/2013/03/io-subystem/chunk-deletes làm tài liệu tham khảo, với các bài kiểm tra hiệu suất và đồ thị:
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
và CHECKPOINT
các bản ghi vẫn đang phát triển. Cảm ơn đã làm rõ điều này.
@Deleted_Rows
với 10000 hoặc bạn có thể kết thúc bằng một vòng lặp vô hạn do nó xóa vô thời hạn các bộ dữ liệu nhỏ. Vì vậy WHILE (@Deleted_Rows = 10000)
- ngay khi không có một "trang" dữ liệu đầy đủ để xóa, nó sẽ dừng lại. Trong triển khai của bạn, WHILE (@Deleted_Rows > 0)
vòng lặp while sẽ thực hiện lại ngay cả khi nó chỉ xóa một hàng và lần thực hiện tiếp theo cũng có thể tìm thấy một hoặc hai hàng để xóa - dẫn đến một vòng lặp vô hạn.
WHILE
vòng lặp : dateadd(MONTH,-7,GETDATE())
.
WHILE
vòng lặp.
Bạn cũng có thể sử dụng GO + bao nhiêu lần bạn muốn thực hiện cùng một truy vấn.
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
là gì để làm việc? Tôi gặp lỗi "Không thể tìm thấy thủ tục được lưu trữ ''" . Không có GO
lệnh, nó hoạt động tốt mặc dù.
@Francisco Goldenstein, chỉ là một sự điều chỉnh nhỏ. CAM KẾT phải được sử dụng sau khi bạn đặt biến, nếu không, WHILE sẽ được thực thi chỉ một lần:
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
Biến thể này của M.Ali đang hoạt động tốt đối với tôi. Nó xóa một số, xóa nhật ký và lặp lại. Tôi đang xem nhật ký phát triển, thả và bắt đầu lại.
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
để xóa tại một thời điểm, và cả WHERE
mệnh đề. Hoạt động như một lá bùa!
Nếu bạn sẵn sàng (và có thể) thực hiện phân vùng, đó là một kỹ thuật hiệu quả để loại bỏ một lượng lớn dữ liệu với ít chi phí thời gian chạy. Không hiệu quả chi phí cho một bài tập một lần, mặc dù.
Tôi đã có thể xóa 19 triệu hàng khỏi bảng 21 triệu hàng trong vài phút . Đây là cách tiếp cận của tôi.
Nếu bạn có khóa chính tăng tự động trên bảng này, thì bạn có thể sử dụng khóa chính này.
Nhận giá trị tối thiểu của khóa chính của bảng lớn trong đó readTime <dateadd (MONTH, -7, GETDATE ()). (Thêm chỉ mục vào readTime, nếu chưa có, chỉ mục này sẽ bị xóa cùng với bảng ở bước 3.). Hãy lưu trữ nó trong một biến 'min_primary'
Chèn tất cả các hàng có khóa chính> min_primary vào bảng phân tầng (bảng bộ nhớ nếu số hàng không lớn).
Thả bàn lớn.
Tái tạo bảng. Sao chép tất cả các hàng từ bảng phân tầng sang bảng chính.
Thả bàn dàn.
Bạn có thể xóa các lô nhỏ bằng cách sử dụng vòng lặp while, đại loại như thế này:
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
Công dụng khác:
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
Không bắt buộc;
Nếu nhật ký giao dịch được bật, hãy tắt nhật ký giao dịch.
ALTER DATABASE dbname SET RECOVERY SIMPLE;
Cú pháp ngắn hơn
select 1
WHILE (@@ROWCOUNT > 0)
BEGIN
DELETE TOP (10000) LargeTable
WHERE readTime < dateadd(MONTH,-7,GETDATE())
END
Nếu bạn đang sử dụng máy chủ SQL 2016 trở lên và nếu bảng của bạn có các phân vùng được tạo dựa trên cột bạn đang cố xóa (ví dụ cột Dấu thời gian), thì bạn có thể sử dụng lệnh mới này để xóa dữ liệu theo phân vùng.
BẢNG TRUNCATE VỚI (PHẦN THAM GIA ({|} [, ... n]))
Điều này sẽ chỉ xóa dữ liệu trong (các) phân vùng đã chọn và là cách hiệu quả nhất để xóa dữ liệu khỏi một phần của bảng vì nó sẽ không tạo nhật ký giao dịch và sẽ được thực hiện nhanh như cắt ngắn thông thường nhưng không xóa tất cả dữ liệu từ cái bàn.
Hạn chế là nếu bảng của bạn không được thiết lập với phân vùng, thì bạn cần phải đi học cũ và xóa dữ liệu theo cách tiếp cận thông thường và sau đó tạo lại bảng với các phân vùng để bạn có thể làm điều này trong tương lai, đó là những gì tôi đã làm. Tôi đã thêm việc tạo và xóa phân vùng vào thủ tục chèn chính nó. Tôi đã có bảng với 500 triệu hàng nên đây là lựa chọn duy nhất để giảm thời gian xóa.
Để biết thêm chi tiết, hãy tham khảo các liên kết dưới đây: https://docs.microsoft.com/en-us/sql/t-sql/statements/truncate-table-transact-sql?view=sql-server-2017
Máy chủ SQL 2016 Bảng rút gọn với các phân vùng
Dưới đây là những gì tôi đã làm trước tiên để xóa dữ liệu trước khi tôi có thể tạo lại bảng với các phân vùng có dữ liệu cần thiết trong đó. Truy vấn này sẽ chạy trong nhiều ngày trong cửa sổ thời gian được chỉ định cho đến khi dữ liệu bị xóa.
: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()
Nếu tôi nói không có vòng lặp, tôi có thể sử dụng GOTO
câu lệnh để xóa số lượng lớn các bản ghi bằng máy chủ sql. exa
IsRepeat:
DELETE TOP (10000)
FROM <TableName>
IF @@ROWCOUNT > 0
GOTO IsRepeat
như cách này, bạn có thể xóa một lượng lớn dữ liệu với kích thước xóa nhỏ hơn.
cho tôi biết nếu cần thêm thông tin