【问题标题】:Remove records by clustered or non-clustered index按聚集或非聚集索引删除记录
【发布时间】:2017-01-24 05:42:50
【问题描述】:

我有一张桌子(比如说ErrorLog

CREATE TABLE [dbo].[ErrorLog]
(
    [Id] [int] IDENTITY(1,1) NOT NULL,
    [Created] [datetime] NOT NULL,
    [Message] [varchar](max) NOT NULL,

    CONSTRAINT [PK_ErrorLog] 
        PRIMARY KEY CLUSTERED ([Id] ASC)
)

我想删除所有超过 3 个月的记录。

我在Created 列上有一个非聚集索引(升序)。

我不确定其中哪一个更好(似乎需要相同的时间)。

查询 #1

DELETE FROM ErrorLog
WHERE Created <= DATEADD(month, - 3, GETDATE())

查询 #2

DECLARE @id INT

SELECT @id = max(l.Id)
FROM ErrorLog l
WHERE l.Created <= DATEADD(month, - 3, GETDATE())

DELETE FROM ErrorLog
WHERE Id <= @id

【问题讨论】:

  • 第一种方法很好
  • 最后一种方法应该更快,因为id 已经有索引,它只有一半宽,...(DateTimes 是 8 个字节,ints 只有 4 个) ,并且可能是聚集的。 - 如果选择的记录是连续的,则使用聚集索引会更快。 Created 真的有时间和日期吗? - 因为Date 列(没有时间)也只有 4 个字节。
  • 对,还有时间部分
  • 我个人会使用第一种方法。它可能会稍微慢一些(甚至可能不明显),但我更愿意明确我的标准,而不是仅仅假设你身份的每个值都会更低。由于目的是在特定时间范围内删除行,因此您应该通过该列中的值而不是标识值来识别这些行。
  • 好吧,既然你说两者之间没有明显区别,我建议你使用第一个。它的意图立即清晰,它需要更少的编码,更容易维护并且在所有情况下都是逻辑正确的。

标签: sql sql-server


【解决方案1】:

一旦您知道要删除的最大集群密钥,那么使用此密钥肯定会更快。问题是是否值得首先使用日期选择此键。正确的决定取决于表的大小以及需要删除的数据部分。表越小,删除的记录数也越小,第一个选项应该越有效(查询#1)。但是,如果要删除的记录数足够大,则 Date 列上的非聚集索引将被忽略,SQL Server 将开始扫描基表。在这种情况下,第二个选项(查询 #2)可能更理想。而且通常还需要考虑其他因素。

我最近解决了类似的问题(从 1.5TB 表中删除了大约 6 亿(2/3)条旧记录),我最终决定采用第二种方法。原因有很多,但主要有以下几点。

在删除旧记录时,该表必须可用于新插入。因此,我无法在一个可怕的删除语句中删除记录,而是必须使用几个较小的批处理以避免锁升级到表级别。较小的批次也将事务日志大小保持在合理的范围内。此外,我每天只有大约一小时的维护窗口,而且不可能在一天内删除所有需要的记录。

考虑到上述情况,对我来说最快的解决方案是根据 Date 列选择我需要删除的最大 ID,然后从聚集索引的开头开始删除,直到一批后选择的 Id另一个(DELETE TOP(@BatchSize) FROM ErrorLog WITH(PAGLOCK) WHERE ID )。我使用 PAGLOCK 提示来增加批量大小而不将锁升级到表级别。最后一天删了好几批。

【讨论】:

    猜你喜欢
    • 2013-08-07
    • 1970-01-01
    • 2016-01-05
    • 2021-01-14
    • 2011-10-12
    • 2020-08-04
    • 2011-04-05
    • 1970-01-01
    相关资源
    最近更新 更多