优化SQL Server上的删除

时间:2009-06-05 11:31:45

标签: sql sql-server

SQL服务器上的

Delete有时很慢,我经常需要优化它们以减少所需的时间。 我一直在寻找有关如何做到这一点的提示,我发现了各种各样的建议。 我想知道你最喜欢和最有效的技术,以驯服删除野兽,以及它们的工作方式和原因。

直到现在:

  • 确保外键具有索引

  • 确保索引的条件

  • 使用WITH ROWLOCK

  • 销毁未使用的索引,删除,重建索引

现在轮到你了。

15 个答案:

答案 0 :(得分:22)

您可能会对以下文章快速订购删除操作感兴趣。

Performing fast SQL Server delete operations

该解决方案侧重于利用视图来简化为批量删除操作生成的执行计划。这是通过引用给定的表一次而不是两次来实现的,这反过来又减少了所需的I / O量。

答案 1 :(得分:13)

我对Oracle有更多的经验,但很可能同样适用于SQL Server:

  • 删除大量行时,发出表锁,因此数据库不必执行大量行锁
  • 如果您删除的表被其他表引用,请确保其他表在外键列上有索引(否则数据库将对每个已删除的行执行全表扫描 在另一个表上确保删除行不违反外键约束)

答案 2 :(得分:9)

我想知道是不是垃圾收集数据库的时候了?您将行标记为删除,服务器稍后在扫描期间将其删除。你不希望每次删除都有这个 - 因为有时一行必须现在去 - 但它偶尔会很方便。

答案 3 :(得分:7)

2014-11-05答案摘要

这个答案被标记为社区维基,因为这是一个不断发展的主题,有很多细微差别,但整体答案很少。

第一个问题是你必须问问自己你正在优化的场景是什么?这通常是在db上具有单个用户的性能,或者在db上具有许多用户的扩展。有时答案恰恰相反。

对于单用户优化

        
  • 提示TABLELOCK
  •     
  • 删除删除中未使用的索引,然后重建它们
  •     
  • 批量使用类似SET ROWCOUNT 20000(或其他任何东西,取决于日志空间)和循环(可能带有WAITFOR DELAY),直到你全部删除它(@@ROWCOUNT = 0
  •     
  • 如果删除大部分的表,只需创建一个新表并删除旧表
  •     
  • 对要删除的行进行分区,然后删除该分区。 [Read more...]

适用于多用户优化

        
  • 提示行锁
  •     
  • 使用聚集索引
  •     
  • 设计聚集索引以在删除大块时最小化页面重组
  •     
  • 更新" is_deleted"列,然后在维护窗口期间稍后进行实际删除

进行一般优化

        
  • 确保FK的源表上有索引
  •     
  • 确保WHERE子句具有索引
  •     
  • 使用视图或派生表标识要在WHERE子句中删除的行,而不是直接引用该表。 [Read more...]

答案 4 :(得分:5)

说实话,从表中删除一百万行的方式与插入或更新一百万行一样严重。问题是行集的大小,你可以做的并不多。

我的建议:

  • 确保该表具有主键和聚簇索引(这对所有操作都至关重要)。
  • 确保聚簇索引能够在删除大块行时进行最少的页面重组。
  • 确保您的选择标准为SARGable。
  • 确保您当前信任所有外键约束。

答案 5 :(得分:4)

(如果索引是“未使用”,为什么它们一直存在?)

我过去使用的一个选项是批量完成工作。粗略的方法是使用SET ROWCOUNT 20000(或其他)和循环(可能带有WAITFOR DELAY)直到你完全摆脱它(@@ ROWCOUNT = 0)。

这可能有助于减少对其他系统的影响。

答案 6 :(得分:4)

问题是你还没有充分定义你的条件。即究竟是什么优化的?

例如,系统是否已进行夜间维护,系统中没有用户?你在删除大部分数据库吗?

如果离线并删除大的%,可能只需构建一个包含要保留的数据的新表,删除旧表并重命名。如果删除小%,您可能希望按日志空间允许的大批量批量处理。它完全取决于您的数据库,但在重建期间丢弃索引可能会有所帮助 - 如果可能的话,由于“离线”而可能会有所帮助。

如果您在线,您的删除与用户活动发生冲突的可能性是什么(用户活动主要是读取,更新还是什么)?或者,您是否尝试优化用户体验或完成查询的速度?如果要从其他用户经常更新的表中删除,则需要批量但批量较小的表。即使您执行类似表锁的操作来强制隔离,如果您的删除语句需要一个小时,那也没有多大用处。

当您更好地定义条件时,可以在此处选择其中一个答案。我喜欢Rob Sanders关于批量处理的帖子中的链接。

答案 7 :(得分:3)

如果您有许多外键表,请从链的底部开始并进行操作。如果没有级联删除的子记录,那么最终的删除会更快并阻止更少的事情(如果我有大量的子表,我将不会打开,因为它会杀死性能)。

批量删除。

如果你有不再使用的外键表(你会惊讶地发现生产数据库最终会因旧表而没有人会摆脱),摆脱它们或者至少打破FK / PK连接。如果没有使用记录,就没有任何意义来为记录查询。

不要删除 - 将记录标记为已删除,然后从所有查询中排除已标记的记录。这是在数据库设计时最好设置的。很多人都使用这个,因为它也是获取记录意外删除的最快最快的方法。但是在现有系统中进行设置需要做很多工作。

答案 8 :(得分:2)

我将在此添加另一个:

确保正确设置事务隔离级别和数据库选项。如果您的SQL服务器设置为不使用行版本控制,或者您正在等待删除行的其他查询上使用隔离级别,那么您可能会在操作发生时为自己设置一些非常差的性能

答案 9 :(得分:2)

在非常大的表上,您有一组非常具体的删除条件,您还可以对表进行分区,切换分区,然后处理删除。

SQLCAT团队一直在真正真正大量数据上使用这种技术。我找到了一些引用here,但我会尝试找到更明确的东西。

答案 10 :(得分:2)

我认为,删除杀死性能的大陷阱是每行删除后的sql,它会更新此行中任何列的所有相关索引。如何在批量删除之前删除所有索引?

答案 11 :(得分:1)

有删除然后删除。如果要将数据作为修剪作业的一部分老化,则希望能够通过聚簇键删除连续的行块。如果你必须使来自不连续的高容量表的数据老化,那将是非常痛苦的。

答案 12 :(得分:1)

如果UPDATES比DELETES更快,您可以在选择中添加一个名为DELETED的状态列并对其进行过滤。然后在晚上运行一个执行实际删除的过程。

答案 13 :(得分:1)

您是否启用了引用完整性的外键? 你有触发器有效吗?

答案 14 :(得分:0)

简化WHERE子句中函数的任何使用!例如:

DELETE FROM Claims
WHERE dbo.YearMonthGet(DataFileYearMonth) = dbo.YearMonthGet(@DataFileYearMonth)

这种形式的WHERE条款需要8分钟才能删除125,837条记录。

YearMonthGet函数用输入日期的年份和月份组成日期,并设置day = 1。这是为了确保我们根据年月而不是月中删除记录。

我将WHERE子句重写为:

WHERE YEAR(DataFileYearMonth) = YEAR(@DataFileYearMonth)
AND MONTH(DataFileYearMonth) = MONTH(@DataFileYearMonth)

结果:删除大约需要38-44秒才能删除这些125,837条记录!