是否有更好的方法来编写基于子查询删除记录的查询?

DDi*_*ita 5 sql t-sql timeout sql-server-2008

我有这个问题:

DELETE from MailingListTable where Md5Hash in (
   SELECT
      dbo.ListItems.Md5Hash
   FROM dbo.Lists
   INNER JOIN dbo.ListItems ON dbo.Lists.Id = dbo.ListItems.ListId
   where dbo.Lists.IsGlobal = 1
 )
Run Code Online (Sandbox Code Playgroud)

MailingListTable是从多个列表动态构建的.然后,我运行上面的查询以删除全局删除列表中的任何列表项.

它在小型设备上并不可怕,但更大的设置可能需要大约5到8分钟(基于我做过的一些测试).我很好奇是否有更好的方式来写这个.我不相信我可以使用删除语句的连接.这就是我选择子查询的原因.

我也尝试过使用EXISTS,但速度要慢得多.common-table expressions因为我使用SQL Server 2008 会更好吗?

Aar*_*and 8

我认为这需要很长时间,因为(a)你正在删除数百万行和(b)你正在把你的日志视为旋转门.这不会神奇地从5-8分钟到5秒,因为您使用EXISTS而不是IN或将子查询更改为CTE或使用JOIN.继续尝试吧,我敢打赌它不会更好:

DELETE ml 
  FROM dbo.MailingListTable AS ml
  INNER JOIN dbo.ListItems AS li
  ON ml.Md4Hash = li.Md5Hash
  INNER JOIN dbo.Lists AS l
  ON l.Id = li.ListId 
  WHERE l.IsGlobal = 1;
Run Code Online (Sandbox Code Playgroud)

问题几乎可以肯定是执行的I/O DELETE,而不是用于识别要删除的行的方法.我敢打赌,SELECT使用完全相同的数据,并在不改变索引结构等,也不管隔离级别确实采取5-8分钟.

那么,如何解决?

首先,确保调整日志以处理该大小的事务.

  • 预先调整日志的大小,使其在这样的操作过程中不必增长,也许可以使您看到的最大尺寸加倍.确切的理想大小不是Stack Overflow上的某个人能够告诉你的.

  • 确保自动增长未设置为10%或1MB等愚蠢的默认值.自动增长应该是一个后备,但是,当你需要时,它应该只发生一次,而不是多次,以涵盖任何特定的活动.因此,请确保它是固定大小(使大小+持续时间可预测)并且大小合理(因此它只发生一次).什么是合理的?不知道 - 太多"它取决于".

  • 禁用任何缩小日志的作业 - 永久.通过反复缩小日志文件来逐个处理失控日志,而不是"防止"日志增长.

接下来,考虑更改您的查询以将那些删除批处理为块.你可以TOP (?)根据行数导致什么样的交易持续时间来参与参数(即使我们确实有更多的信息,也没有神奇的公式).

CREATE TABLE #x
(
  Md5Hash SOME_DATA_TYPE_I_DO_NOT_KNOW PRIMARY KEY
);

INSERT #x SELECT DISTINCT li.Md5Hash
  FROM dbo.ListItems AS li
  INNER JOIN dbo.Lists AS l
  ON l.Id = li.ListId 
  WHERE l.IsGlobal = 1;

DECLARE @p TABLE(p INT SOME_DATA_TYPE_I_DO_NOT_KNOW PRIMARY KEY);

SELECT @rc = 1;

WHILE @rc > 0
BEGIN
  DELETE @p;

  DELETE TOP (?)  
    OUTPUT deleted.Md5Hash INTO @p
    FROM #x;

  SET @rc = @@ROWCOUNT;

  BEGIN TRANSACTION;    

    DELETE ml FROM dbo.MailingListTable AS ml
    WHERE EXISTS (SELECT 1 FROM @p WHERE Md5Hash = ml.Md5Hash);

  COMMIT TRANSACTION;
  -- to minimize log impact you may want to CHECKPOINT
  -- or backup the log here, every loop or every N loops
END
Run Code Online (Sandbox Code Playgroud)

这可能会延长操作所花费的总时间(特别是如果您在每个循环上备份或检查点,或使用WAITFOR或两者都添加人工延迟),但应允许其他事务在块之间潜入,等待更短的事务整个过程.此外,由于您对日志的个人影响较小,实际上最终可能会更快完成.但我必须假设的问题不在于它需要5-8分钟,这可能需要花费5-8分钟块.这应该会大大减轻(如果确实如此,你为什么要关心需要多长时间?).

我在这里写了很多关于这种技术的文章.