我有这个问题:
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
)
MailingListTable是从多个列表动态构建的。然后,我运行上面的查询以删除全局删除列表中的任何列表项。
小套装并不可怕,但更大的套装可能需要5到8分钟(基于我做的一些测试)。我很好奇是否有更好的方式来写这个。我不相信我可以使用删除语句的连接。这就是我选择子查询的原因。
我也尝试过使用EXISTS
,但速度要慢得多。因为我使用SQL Server 2008,所以使用common-table expressions
会更好吗?
答案 0 :(得分: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;
问题几乎可以肯定是执行DELETE
所涉及的I / O,而不是用于标识要删除的行的方法。我打赌SELECT
使用完全相同的数据而不改变索引结构等。无论隔离级别 NOT 需要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
这可能会延长操作所花费的总时间(特别是如果您在每个循环上备份或检查点,或使用WAITFOR
或两者添加人为延迟),但应允许其他事务潜入在块之间,等待更短的事务而不是整个过程。此外,由于您对日志的个人影响较小,实际上最终可能会更快完成。但我必须假设问题不是需要5-8分钟,可能需要5-8分钟和块。这应该会大大减轻(如果确实如此,你为什么要关心需要多长时间?)。