我有一个从表中删除大量数据的查询,如下所示.它使用WHILE循环来try 不销毁事务日志(log),但是Customers表中有大约2亿条记录,并且它正在删除大约.200万.我想知道替换Not Existes是否会有帮助.

WHILE (1=1)
BEGIN
  DELETE TOP(10000) FROM Customers
  WHERE NOT EXISTS (SELECT * FROM CustomerInvoices WHERE CustomerInvoices.CustomerId = 
  Customers.CustomerId)
  IF (@@ROWCOUNT = 0)
  BREAK
END

推荐答案

您的问题是,在找到与NOT EXISTS匹配的10000行之前,它需要判断的客户行数每批都在增加.

匹配行的比率将稳步下降,直到最后一批您扫描全部1.98亿行以找到最后10,000行.

你在做200个批次.平均每个批次读取Customers行中的1亿行(最早的批次要少得多,后面的批次更多)-仅从该表读取的总行就达到200亿行,在CustomerInvoices行中读取的行数也差不多.

如果执行计划是连续扫描,那么很可能每个批次都会判断所有已经处理过的批次,并在最终到达感兴趣的批次之前发现不符合条件.

您可以创建具有顺序整型列的临时表...

DECLARE @LastRow INT

CREATE TABLE #DeleteCandidates(Id int PRIMARY KEY, CustomerId INT);

INSERT #DeleteCandidates
SELECT ROW_NUMBER()
         OVER (
           ORDER BY (SELECT 0)) AS Id,
       Customers.CustomerId
FROM   Customers
WHERE  NOT EXISTS (SELECT *
                   FROM   CustomerInvoices
                   WHERE  CustomerInvoices.CustomerId = Customers.CustomerId)

SET @LastRow = @@ROWCOUNT 

然后编写一些代码来处理包含Id个范围的<batch_size>个块的临时表.

例如,如下所示

DECLARE @BatchSize INT = 10000
DECLARE @MinId INT = 1

WHILE @MinId <= @LastRow
  BEGIN

      DELETE FROM Customers
      WHERE  Customers.CustomerId IN (SELECT dc.CustomerId
                                      FROM   #DeleteCandidates dc
                                      WHERE  dc.Id >= @MinId
                                             AND dc.Id < @MinId + @BatchSize)
             AND NOT EXISTS (SELECT *
                             FROM   CustomerInvoices/*WITH (HOLDLOCK )*/
                             WHERE  CustomerInvoices.CustomerId = Customers.CustomerId)

      SET @MinId = @MinId + @BatchSize
  END 

如果有插入,您仍然需要在实际的DELETE上加上NOT EXISTS,因为该标识意味着删除候选者不再符合条件.

您还可以考虑使用HOLDLOCK提示来处理DELETE查询本身正在运行时真正并发插入的可能性.

Sql相关问答推荐

表名数组

如何更改函数返回的列名?

如何用客户名称计算sum(dr)和sum(cr)

使用generate_series()时,LEFT联接缺少日期/间隔

我需要一个regexp_like来只验证字母D或T、数字和管道

使用SQL数据库中的现有列派生或修改几个列

此过程如何在不引用传递的参数值的情况下执行工作?

删除所有订单中可用的重复值

SQL按组 Select 最小值,当值不存在时为Null

统计重复记录的总数

具有多个表 JOINS 的 STRING_AGG 的替代方法 (SQL Server 2016)

SQL 搜索 - 获取最大值日期的奇怪行为

SQL Server - 复杂场景 - 比较状态并填充值到后续行

汇总具有连续日期范围的行

插入行时的行安全策略问题

MS ACCESS 错误插入 X(...) 从 A 联合 Select ... 从 B

Postgresql 需要一个查询,为我提供所有没有具有特定状态值的子元素的父母

计算 SQL 中的总体成功率:递归 CTE 还是替代方法?

如何在 SQL Server 中参数化 Select top 'n'

删除具有相同 ID 的重复记录 - Postgresql