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)
VendorId
是PK和non-clustered,其中此脚本未使用聚集索引。还有 5 个其他非唯一、非聚集索引。
任务是“删除另一个表中不存在的供应商”并将它们备份到另一个表中。我有3张桌子,vendors, SpecialVendors, SpecialVendorBackups
。试图删除表中SpecialVendors
不存在的内容Vendors
,并备份已删除的记录,以防万一我做错了,我必须在一两周内将它们放回原处。
执行计划显示它正在以某种顺序从非聚集索引读取行,然后对读取的每个外部行执行查找以评估 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 EXISTS
在DELETE
查询本身中包含 a以适应更新,因为临时表已填充,但这应该会更有效率,因为它每批只需要执行 4,500 次搜索。