删除行而不会使数据库超载

问题描述 投票:0回答:1

该表每年包含约 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

sql sql-server database-administration
1个回答
0
投票

我们无法告诉您系统在不影响性能或阻塞的情况下可以容忍多大的批量。您需要根据您的观察/监控来找出答案。

随着时间的推移,你可以使用这样的方法来蚕食你的桌子。有旋钮和开关可以进行调整,但您需要在删除语句中提供具体标准。

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;
© www.soinside.com 2019 - 2024. All rights reserved.