web-dev-qa-db-ja.com

SQL Server 14.0で数百万のレコードを削除する

それぞれ200ミリオンのレコードを含む2つのテーブルがあります。列の整数値に基づいて、約70百万のレコードを削除する必要があります。

次のスクリプトを使用して、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

TableAには、6つの列、5つの整数と1つのNVARCHAR(64)が含まれています。列Codeにはインデックスがあり、PKにはclusterIndexがあります。 TableBには、Codeという1つの列しか含まれていません。これはPKです。

数時間スクリプトを実行した後、非常に遅くなりました。

最初は各反復が250ミリ秒で実行された後、数時間実行すると2分に増加します。

データベースは単純復旧モードです。誰も使用しておらず、256 GBのRAMを搭載した専用マシンで実行されています。

私は1時間ごとにインデックスを再構築しようとしましたが、データベースを縮小しました(ユーザーができないためファイルではありません)が、常に遅いです。

別のテーブルのレコードを削除し始めると、それはまったく同じ動作で、非常に速く開始され、その後、反復ごとに増加してスローダウンします。

初期状態に戻すにはどうすればよいですか?削除を改善するにはどうすればよいですか?私がしようとしました

6
Giox

バッチで大規模な削除を行う場合は、クラスター化インデックスシークをプランで使用できるように、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
11
Dan Guzman