从 SQL 表中删除数百万行

cil*_*ler 9 performance sql-server delete query-performance

我必须从 221+ 百万行表中删除 16+ 百万条记录,而且执行速度非常慢。

如果您分享使以下代码更快的建议,我将不胜感激:

SET TRANSACTION ISOLATION LEVEL READ COMMITTED;

DECLARE @BATCHSIZE INT,
        @ITERATION INT,
        @TOTALROWS INT,
        @MSG VARCHAR(500);
SET DEADLOCK_PRIORITY LOW;
SET @BATCHSIZE = 4500;
SET @ITERATION = 0;
SET @TOTALROWS = 0;

BEGIN TRY
    BEGIN TRANSACTION;

    WHILE @BATCHSIZE > 0
        BEGIN
            DELETE TOP (@BATCHSIZE) FROM MySourceTable
            OUTPUT DELETED.*
            INTO MyBackupTable
            WHERE NOT EXISTS (
                                 SELECT NULL AS Empty
                                 FROM   dbo.vendor AS v
                                 WHERE  VendorId = v.Id
                             );

            SET @BATCHSIZE = @@ROWCOUNT;
            SET @ITERATION = @ITERATION + 1;
            SET @TOTALROWS = @TOTALROWS + @BATCHSIZE;
            SET @MSG = CAST(GETDATE() AS VARCHAR) + ' Iteration: ' + CAST(@ITERATION AS VARCHAR) + ' Total deletes:' + CAST(@TOTALROWS AS VARCHAR) + ' Next Batch size:' + CAST(@BATCHSIZE AS VARCHAR);             
            PRINT @MSG;
            COMMIT TRANSACTION;
            CHECKPOINT;
        END;
END TRY
BEGIN CATCH
    IF @@ERROR <> 0
       AND @@TRANCOUNT > 0
        BEGIN
            PRINT 'There is an error occured.  The database update failed.';
            ROLLBACK TRANSACTION;
        END;
END CATCH;
GO
Run Code Online (Sandbox Code Playgroud)

执行计划(限制为 2 次迭代)

在此处输入图片说明

VendorIdPKnon-clustered,其中此脚本未使用聚集索引。还有 5 个其他非唯一、非聚集索引。

任务是“删除另一个表中不存在的供应商”并将它们备份到另一个表中。我有3张桌子,vendors, SpecialVendors, SpecialVendorBackups。试图删除表中SpecialVendors不存在的内容Vendors,并备份已删除的记录,以防万一我做错了,我必须在一两周内将它们放回原处。

Mar*_*ith 8

执行计划显示它正在以某种顺序从非聚集索引读取行,然后对读取的每个外部行执行查找以评估 NOT EXISTS

在此处输入图片说明

您正在删除表的 7.2%。16,000,000 行 3,556 批次 4,500

假设符合条件的行均匀分布在整个索引中,那么这意味着它将每 13.8 行删除大约 1 行。

因此,迭代 1 将读取 62,156 行并在找到要删除的 4,500 行之前执行那么多索引查找。

迭代 2 将读取 57,656 (62,156 - 4,500) 行,这些行绝对不会忽略任何并发更新(因为它们已经被处理),然后再读取 62,156 行以删除 4,500。

迭代 3 将读取 (2 * 57,656) + 62,156 行,依此类推,直到最终迭代 3,556 将读取 (3,555 * 57,656) + 62,156 行并执行如此多的查找。

所以在所有批次中执行的索引搜索次数是 SUM(1, 2, ..., 3554, 3555) * 57,656 + (3556 * 62156)

这是((3555 * 3556 / 2) * 57656) + (3556 * 62156)- 或364,652,494,976

我建议您先将要删除的行具体化到临时表中

INSERT INTO #MyTempTable
SELECT MySourceTable.PK,
       1 + ( ROW_NUMBER() OVER (ORDER BY MySourceTable.PK) / 4500 ) AS BatchNumber
FROM   MySourceTable
WHERE  NOT EXISTS (SELECT *
                   FROM   dbo.vendor AS v
                   WHERE  VendorId = v.Id) 
Run Code Online (Sandbox Code Playgroud)

并更改DELETE为 deleteWHERE PK IN (SELECT PK FROM #MyTempTable WHERE BatchNumber = @BatchNumber)您可能仍需要NOT EXISTSDELETE查询本身中包含 a以适应更新,因为临时表已填充,但这应该会更有效率,因为它每批只需要执行 4,500 次搜索。