2009-06-05 65 views
36

Delete在sql server上有时很慢,我经常需要优化它们以减少所需的时间。 我一直在googleing有点寻找如何做到这一点的提示,并且我发现了不同的建议。 我想知道你最喜欢的和最有效的技术来驯服这只被删除的野兽,以及它们是如何工作的,以及它们为什么工作的。在SQL Server上优化删除

直到如今

  • 一定的外键具有指标

  • 是确保那里的条件被索引

    使用WITH ROWLOCK

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

现在,轮到你了。

+0

向高级用户提出的问题:这个问题没有单一的答案,更像是一种知识库,而不是一个简单的问题答案。可能它可以变成一个社区维基? (如果我很了解c.w.是什么) – pomarc 2009-06-10 11:19:43

+0

我希望将其作为一个正在进行的总结。这篇文章对我非常有用,但花了数小时的时间来阅读这些建议。我已经提交了一个新的摘要被拒绝的编辑,等待看看第二次尝试是否通过:) – xero 2014-11-20 22:45:45

+0

@xero我回滚了你的编辑,你可以在阅读后标记管理员注意(使用其他)社区维基警察被关闭?](http://meta.stackexchange.com/questions/392/should-the-community-wiki-police-be-shut-down)[我们可以做些什么来使社区维基更好? ](http://meta.stackexchange.com/questions/67039/what-c​​an-we-do-to-make-community-wiki-better)和http://meta.stackoverflow.com/a/266921 – bummi 2014-11-21 00:12:01

回答

21

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

Performing fast SQL Server delete operations

溶液集中在利用为了简化用于成批删除操作中产生的执行计划的视图。这是通过参考一次给定的表来实现的,而不是两次,这又减少了所需的I/O量。

12

我与甲骨文更多的经验,但很可能同样适用于SQL Server,以及:

删除大量行的,问题表锁的时候,所以数据库没有
  • 做大量的行锁
  • 如果你从中删除的表被其他表引用,请确保这些其他表在外键列上有索引(否则数据库将为每个删除执行全表扫描确保删除行不违反外键约束)
+2

表锁会阻止在表上插入和更新,在其他事务开始超时之前需要确保删除速度快。 – dsum 2011-12-07 23:20:17

+0

dsum:true,但删除大量记录通常发生在没有其他活动(例如夜间)的维护时段中。 – 2011-12-08 21:59:13

4

(如果该索引是“未使用”,他们为什么没有呢?)我已经在过去使用

一种选择是做批量的工作。粗略的方法是使用SET ROWCOUNT 20000(或其他)和循环(或许与WAITFOR DELAY),直到你摆脱它(@@ ROWCOUNT = 0)。

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

5

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

我的建议:

  • 确保表中有一个主键和聚集索引(这是所有操作至关重要)。
  • 确保聚集索引是这样的:如果要删除大块行,则会发生最小的页面重组。
  • 确保您的选择标准是SARGable。
  • 请确保您的所有外键约束当前都是可信的。
2

我会添加一个又一个这样的:

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

2

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

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

3

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

分批删除。

如果你有不再使用的外键表(你会惊讶地发现生产数据结束时老表没有人会摆脱),摆脱它们或者至少打破FK/PK连接。如果没有被使用,没有任何意义可以为表格记录记录。

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

1

有删除,然后有删除。如果您将数据作为修剪作业的一部分去除,则希望能够通过聚簇键删除连续的行块。如果您必须从不连续的高容量表中删除数据,这是非常非常痛苦的。

1

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

9

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

4

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

例如,夜间维护系统是否关闭,系统中没有用户?你是否删除了大量的数据库?

如果脱机并删除一个大的%,可能有意义,只需建立一个新数据表保留,删除旧表并重命名。如果删除一个很小的百分比,你可能希望按照日志空间允许的大批量进行批处理。它完全取决于你的数据库,但是在重建期间删除索引可能会伤害或帮助 - 如果甚至因“脱机”而可能。

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

当您更好地定义您的条件时,您可以在此选择其中一个答案。我喜欢罗伯·桑德斯的帖子中的链接,用于分配物品。

1

您是否有激活参照完整性的外键? 你有触发器是否有效?

2

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

5

通过2014年11月5日

这个答案被标记为社区维基,因为这是一个有很多细微之处的一个不断发展的主题,但总体极少可能的答案回答的总结。

第一个问题是你必须问自己你正在优化什么场景?这通常或者是单个用户在数据库上的性能,或者是在数据库上有许多用户的规模。有时答案是完全相反的。

对于单个用户优化

  • 提示一个TABLELOCK
  • 删除索引中未使用的删除再重新编译他们后来
  • 使用类似SET ROWCOUNT 20000(或什么的,这取决于日志空间)批和循环(也许与WAITFOR DELAY),直到你摆脱它(@@ROWCOUNT = 0
  • 如果删除大%的表,只是马ke新一个并删除旧表
  • 分区要删除的行,然后删除parition。[Read more...]

对于多用户优化

  • 提示行锁
  • 使用聚集索引
  • 设计聚集索引,以尽量减少页面重新组织如果大块被删除
  • 更新“is_deleted”列,然后在维护窗口中稍后进行实际删除

对于一般的优化

  • 确保FKS对他们的源表的索引
  • 确保WHERE子句指标
  • 识别WHERE子句中删除与视图中的行或派生表而不是直接引用表。 [Read more...]
-1

在您的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秒,以删除这些记录125837!