2011-12-29 150 views
7

对SQL语句中要删除的行数应该设置什么限制?SQL DELETE - 最大行数

我们需要删除1到几十万行,并且需要应用某种最佳实践限制,以便在每次我们清空垃圾篮时不会绝对杀死SQL服务器或填写日志。

此问题不是特定于任何类型的数据库。

+0

这样模糊的问题无法回答。没有银弹 – zerkms 2011-12-29 20:59:12

+0

我们使用批量夜间移动作业将记录从一个表格移动到另一个表格。我们运行的批量为10,000,性能影响可以忽略不计。 – Dimitri 2011-12-29 21:01:51

+0

您需要尝试添加一些限制并监控服务器性能以获得最佳答案。 – piotrekkr 2011-12-29 21:01:54

回答

12

这是一个非常广泛的问题,基本归结为“它取决于”。影响它的因素包括:

  • 什么是您的并发级别?删除语句在受影响的行上放置排他锁。取决于数据库引擎,删除的数据分布等,可能会升级到页面或整个表格。您的数据读取器在删除期间是否可以被阻止?

  • 删除语句有多复杂?你还加入了多少个其他表,还是有复杂的WHERE子句?有时,删除行的标识可能比删除本身更“昂贵”,因此一个大的删除可能会“更便宜”。

  • 你是否害怕死锁?当你减小删除的大小时,你的死锁“脚印”就会减少。理想情况下,单行删除将始终成功。

  • 你关心吞吐量性能吗?与任何SQL语句一样,通常会有一定量的开销(连接内容,查询解析,返回结果等)。从单一连接的角度来看,1000行删除将比1000 x 1行删除更快。

  • 不要忘记索引维护开销,碎片清理或任何触发器。它们也会影响你的系统。

总的来说,我的基准线每条语句有1000行。与我合作过的大多数系统(子企业)最终每删除500到5000条记录的甜点。我喜欢做这样的事情:

set rowcount 500 

select 1 -- Just to force @@rowcount > 0 
while @@ROWCOUNT > 0 
delete from [table] 
    [where ...] 
+0

+1,会补充说,如果你删除了一定数量的行,有时候更好地处理它;所以删除10然后100然后1000作为数据库缓存了一些工作,这kind'a你点4 – Ben 2011-12-29 22:33:24

+0

你适合在一般有很多更多的支持代码做什么,但一个“虚拟”斜坡上升。我还发现,试图达到“最大”行/秒删除是相当无用的。更好的方法是找到一个可接受的窗口(比如15秒),并尝试一次删除多少行。在注释伪代码中:挑选100行以删除。删除并获取时间信息。如果时间<15秒,则选择ROWS * 1.5删除;否则选择行* 0.5删除。重复。这将扩大您的基于时间的吞吐量,并且对数据库上的其他活动很敏感。 – jklemmack 2012-01-03 20:33:20

+0

对于未来的读者,请参阅下面的@SQLPhil的答案。基于Microsoft Books Online,未来版本的SQL Server将不会授予'INSERT','UPDATE'或'DELETE'语句的'SET ROWCOUNT'。最好使用'TOP'语法。 – jklemmack 2016-12-06 15:48:16

0

一般的答案是删除该表并重新创建它,这是一个性能良好的解决方案,而是适用于全表

+0

如果有过程,函数,视图等依赖于你要删除表时会发生什么?根本没有任何理由,你会使他们无效。 – Ben 2011-12-29 22:30:59

1

除非你有很多触发器或完整性约束的验证,删除不该手术费用不高。

但是,如果你关心性能,我最初的预感是将相应的行标记为已删除,然后在定期清理期间将其物理删除。但是我并不是很喜欢这个,因为你必须改变该表上的任何查询来排除逻辑上的 - 但不是物理上删除的行。

1

每当我看到经常删除大量行的散装数据库,这让我想起了数据模型或处理的设计是不是最佳的。为什么加载100万行然后删除它们?如果您需要执行诸如清除历史数据之类的操作,请考虑表分区。

+1

罗素。可能有一些原因可能会导致您载入一百万条记录;您可以在丢弃原始数据之前执行计算并存储计算的值。但你确实有一个好点,适当的设计很重要。 – Leons 2011-12-31 17:41:36

4

虽然限制影响的行数您删除使用SET ROWCOUNT选项,然后进行一个循环是非常好的(和我以前很多时候用它),要知道,从2012年起,SQL这不会是一个选项(见BOL)。

因此,另一个选择可以是限制使用TOP子句被删除的行数。即

SELECT 1 

WHILE @@ROWCOUNT > 0 
BEGIN 
    DELETE TOP (#) 
    FROM mytable 
    [WHERE ...] 
END