删除 SQL Server 14.0 上的数百万条记录

Gio*_*iox 7 sql-server delete sql-server-2017

我有两个表,每个表包含 2 亿条记录。我必须根据列中的整数值从它们中删除大约 7000 万条记录。

我使用以下脚本以 4000 块为单位删除它们:

DECLARE @BATCHSIZE INT, @ITERATION INT, @TOTALROWS INT, @MSG VARCHAR(500)
DECLARE @STARTTIME DATETIME, @ENDTIME DATETIME

SET NOCOUNT ON;
SET DEADLOCK_PRIORITY LOW;
SET @BATCHSIZE = 4000
SET @ITERATION = 0 
SET @TOTALROWS = 0 

WHILE @BATCHSIZE>0
BEGIN   
    SET @STARTTIME = GETDATE();

    BEGIN TRANSACTION
    DELETE TOP(@BATCHSIZE)
    FROM [mydb].[dbo].tableA 
    WHERE [mydb].[dbo].tableA.Code not IN (
            SELECT Code
            FROM [mydb].[dbo].TableB)

    SET @BATCHSIZE=@@ROWCOUNT
    SET @ITERATION=@ITERATION+1
    SET @TOTALROWS=@TOTALROWS+@BATCHSIZE
    COMMIT TRANSACTION;

    SET @ENDTIME = GETDATE();

    SET @MSG = 'Iteration: ' + CAST(@ITERATION AS VARCHAR) + ' Total deletes:' + CAST(@TOTALROWS AS VARCHAR) + '  >> ' + CAST(DATEDIFF(millisecond, @STARTTIME,@ENDTIME) AS VARCHAR)
    RAISERROR (@MSG, 0, 1) WITH NOWAIT
END
Run Code Online (Sandbox Code Playgroud)

表 A 包含 6 列,5 个整数和一个 NVARCHAR(64)。Code列上有索引,PK上有clusterIndex。TableB 只包含一列Code,它是一个PK。

运行脚本几个小时后,它变得非常非常慢。

开始时每次迭代在 250 毫秒内执行,然后在运行几个小时后增加到2 分钟

数据库处于简单恢复模式。它没有被任何人使用,它在具有 256GB RAM 的专用机器上运行。

我试图每小时重建索引,缩小数据库(不是文件,因为我的用户不能)但它总是很慢。

如果我开始删除另一个表上的记录,它具有完全相同的行为,开始非常快,然后在每次迭代后增加到减慢。

如何恢复初始条件?我可以做些什么来改善删除?我试过

Dan*_*man 11

对于批量的大删除,考虑指定一个聚集索引键范围而不是使用,TOP以便在计划中可以使用聚集索引查找。下面是一个例子。

DECLARE
      @BATCHSIZE INT = 4000
    , @ITERATION INT = 0
    , @TOTALROWS INT = 0
    , @MSG VARCHAR(500)
    , @STARTTIME DATETIME
    , @ENDTIME DATETIME
    , @StartValue int = 0
    , @EndValue int = 0
    , @MaxValue int = (SELECT MAX(PK) FROM [mydb].[dbo].tableA);

SET NOCOUNT ON;
SET DEADLOCK_PRIORITY LOW;

WHILE @StartValue <= @MaxValue
BEGIN

    SET @EndValue = @StartValue + @BATCHSIZE;

    SET @STARTTIME = GETDATE();

    DELETE FROM [mydb].[dbo].tableA 
    WHERE [mydb].[dbo].tableA.Code NOT IN (
            SELECT Code
            FROM [mydb].[dbo].TableB
        )
        AND [mydb].[dbo].tableA.PK >= @StartValue
        AND [mydb].[dbo].tableA.PK < @EndValue;

    SET @TOTALROWS=@TOTALROWS+@@ROWCOUNT;
    SET @ITERATION=@ITERATION+1;


    SET @ENDTIME = GETDATE();

    SET @MSG = 'Iteration: ' + CAST(@ITERATION AS VARCHAR) + ' Total deletes:' + CAST(@TOTALROWS AS VARCHAR) + '  >> ' + CAST(DATEDIFF(millisecond, @STARTTIME,@ENDTIME) AS VARCHAR);

    RAISERROR (@MSG, 0, 1) WITH NOWAIT;

    SET @StartValue = @EndValue;

END;
GO
Run Code Online (Sandbox Code Playgroud)