该表每年包含约 1 亿行数据。
我想删除除过去 2 年数据之外的所有数据。
数据从2017年开始。
为此,我使用 while 循环来删除前 10,000 行,而不是简单的删除。
在这个过程中,我在想,如何观察每个循环删除的最佳行数?
我怎样才能做到这一点而不在事务日志中留下痕迹?
我想以最有效的方式循环删除而不给数据库增加负担。
DECLARE
@TargetDT VARCHAR(10),@Counter INT = 1
SET @TargetDT = '2018-01-01'
WHILE (@Counter <= 100)
BEGIN
delete top(10000) from MYTABLE where DATE_COLUMN < @TargetDT
SET @Counter = @Counter + 1
END
我尝试了每个循环删除不同的行数:500、1000、2000、5000、10000
我们无法告诉您系统在不影响性能或阻塞的情况下可以容忍多大的批量。您需要根据您的观察/监控来找出答案。
随着时间的推移,你可以使用这样的方法来蚕食你的桌子。有旋钮和开关可以进行调整,但您需要在删除语句中提供具体标准。
DECLARE @DummyTable TABLE (DateTime DATETIME); INSERT INTO @DummyTable SELECT TOP 90 DATEADD(YEAR,-1,GETDATE()) FROM sys.sysobjects;
/* ---- \\ ---- || ---- // ---- */
DECLARE @Message NVARCHAR(MAX), @StartTime TIME, @EndTime TIME, @BatchSize INT, @StartDateTime DATETIME = GETDATE(), @TotalRowCount INT = 0, @RowCount INT,
@DelaySeconds INT, @DelayTime NVARCHAR(9), @ScriptName NVARCHAR(50), @OutputToLog BIT;
SET NOCOUNT ON;
SET @ScriptName = 'Demo Prune Script';
SET @StartTime = '13:00:00';
SET @EndTime = '05:00:00';
SET @BatchSize = 35;
SET @DelaySeconds = 5;
SET @OutputToLog = 1;
SET @Message = N'['+RIGHT(CONVERT(NVARCHAR(19),GETDATE(),121),14)+N']: '+@ScriptName+N' script initiated at '+CONVERT(NVARCHAR(19),GETDATE(),121)+N' as '+SYSTEM_USER+' with parameters:
StartTime: '+CONVERT(NVARCHAR(8),@StartTime,121)+N'.
EndTime: '+CONVERT(NVARCHAR(8),@EndTime,121)+N'.
BatchSize: '+CONVERT(NVARCHAR(8),@BatchSize)+N'.
DelaySeconds: '+CONVERT(NVARCHAR(8),@DelaySeconds,121)+N'.
';
IF @OutputToLog = 1 BEGIN RAISERROR (@Message, 0, 0) WITH NOWAIT, LOG; END
IF @OutputToLog = 0 BEGIN RAISERROR (@Message, 0, 0) WITH NOWAIT; END
BEGIN TRY
WHILE CASE WHEN @StartTime < @EndTime AND CAST(GETDATE() AS TIME) BETWEEN @StartTime AND @EndTime THEN 1
WHEN @EndTime < @StartTime AND CAST(GETDATE() AS TIME) >= @StartTime OR CAST(GETDATE() AS TIME) < @EndTime THEN 1
ELSE 0 END = 1
AND (@RowCount = @BatchSize OR @RowCount IS NULL)
BEGIN
DELETE TOP (@BatchSize)
FROM @DummyTable
WHERE DateTime < DATEADD(YEAR,-0,GETDATE());
SET @RowCount = @@ROWCOUNT;
SET @TotalRowCount+=@RowCount;
IF (@RowCount > 0)
BEGIN
SET @Message = N'['+RIGHT(CONVERT(NVARCHAR(19),GETDATE(),121),14)+N']: '+@ScriptName+N' has deleted '+ CAST(@RowCount AS NVARCHAR(10)) + N' row(s).';
IF @Rowcount = @BatchSize SET @Message += N' Beginning delay of '+CAST(@DelaySeconds AS NVARCHAR(5))+N' second(s). ';
SET @DelayTime = CONVERT(NVARCHAR(8),DATEADD(SECOND,@DelaySeconds,CAST('00:00' AS TIME)),121);
IF @OutputToLog = 1 RAISERROR (@Message, 0, 0) WITH NOWAIT, LOG;
IF @OutputToLog = 0 RAISERROR (@Message, 0, 0) WITH NOWAIT;
IF @Rowcount = @BatchSize WAITFOR DELAY @DelayTime;
END
END;
IF @RowCount = 0
BEGIN
SET @Message = N'['+RIGHT(CONVERT(NVARCHAR(19),GETDATE(),121),14)+N']: '+N''+@ScriptName+N' script has run out of rows to delete.';
IF @OutputToLog = 1 RAISERROR (@Message, 0, 0) WITH NOWAIT, LOG;
IF @OutputToLog = 0 RAISERROR (@Message, 0, 0) WITH NOWAIT;
END;
IF CASE WHEN @StartTime < @EndTime AND CAST(GETDATE() AS TIME) BETWEEN @StartTime AND @EndTime THEN 1
WHEN @EndTime < @StartTime AND CAST(GETDATE() AS TIME) >= @StartTime OR CAST(GETDATE() AS TIME) < @EndTime THEN 1
ELSE 0 END != 1
BEGIN
SET @Message = N'['+RIGHT(CONVERT(NVARCHAR(19),GETDATE(),121),14)+N']: '+N''+@ScriptName+N' script is currently outside of it''s operational hours ('+CONVERT(NVARCHAR(8),@StartTime,121)+N' to '+CONVERT(NVARCHAR(8),DATEADD(SECOND,-1,@EndTime),121)+N') and has been stopped.';
IF @OutputToLog = 1 RAISERROR (@Message, 0, 0) WITH NOWAIT, LOG;
IF @OutputToLog = 0 RAISERROR (@Message, 0, 0) WITH NOWAIT;
END;
SET @Message = N'['+RIGHT(CONVERT(NVARCHAR(19),GETDATE(),121),14)+N']: '+N''+@ScriptName+N' script ran for '+RIGHT('000' + CAST(DATEDIFF(SECOND,@StartDateTime, GETDATE()) / 3600 AS NVARCHAR(2)), 2)+N':'+RIGHT('00' + CAST(DATEDIFF(SECOND,@StartDateTime, GETDATE()) % 3600 / 60 AS NVARCHAR(2)), 2)+N':'+RIGHT('00' + CAST(DATEDIFF(SECOND,@StartDateTime, GETDATE()) % 60 AS NVARCHAR(2)), 2)+N'.
It deleted '+CAST(@TotalRowCount AS NVARCHAR(10))+N' row(s).';
IF @OutputToLog = 1 RAISERROR (@Message, 0, 0) WITH NOWAIT, LOG;
IF @OutputToLog = 0 RAISERROR (@Message, 0, 0) WITH NOWAIT;
END TRY
BEGIN CATCH
DECLARE @ErrorMessage NVARCHAR(MAX) = ERROR_MESSAGE(), @ErrorProcedure NVARCHAR(128) = ERROR_PROCEDURE(), @ErrorLine INT = ERROR_LINE(), @ErrorState INT = ERROR_STATE(), @ErrorSeverity INT = ERROR_SEVERITY(), @ErrorNumber INT = ERROR_NUMBER();
SET @Message = N'['+RIGHT(CONVERT(NVARCHAR(19),GETDATE(),121),14)+N']: '+N''+@ScriptName+N' encountered an error and was unable to continue:
Message: '+COALESCE(@ErrorMessage,'')+N'
Procedure: '+COALESCE(@ErrorProcedure,'')+N'
Line: '+COALESCE(CAST(@ErrorLine AS NVARCHAR(10)),'')+N'
It ran for '+RIGHT('000' + CAST(DATEDIFF(SECOND,@StartDateTime, GETDATE()) / 3600 AS NVARCHAR(2)), 2)+N':'+RIGHT('00' + CAST(DATEDIFF(SECOND,@StartDateTime, GETDATE()) % 3600 / 60 AS NVARCHAR(2)), 2)+N':'+RIGHT('00' + CAST(DATEDIFF(SECOND,@StartDateTime, GETDATE()) % 60 AS NVARCHAR(2)), 2)+N'.
It deleted '+CAST(@TotalRowCount AS NVARCHAR(10))+N' row(s).';
IF @OutputToLog = 1 RAISERROR (@Message, 0, 0) WITH NOWAIT, LOG;
IF @OutputToLog = 0 RAISERROR (@Message, 0, 0) WITH NOWAIT;
THROW;
END CATCH;
SET NOCOUNT OFF;