从SQL Server中删除大量行 - 以高效且非锁定的方式

时间:2011-05-05 21:55:16

标签: sql sql-server-2005 tsql

我正在编写一个过程来删除n天内几张表中的所有行。

简单的简单查询很容易编写

DELETE FROM [myTable] 
WHERE [Created] < GETDATE()-30

一个问题是日期字段上没有索引 - 我可以添加一个,但我正在通过做类似的事情来解决它:

SELECT @var = MAX([ID]) FROM myTable WHERE Created < GETDATE()-30; 
DELETE FROM myTable WHERE ID < @var

这看起来像是一种可接受的方法吗?

问题是表格很大,而且每次运行时此查询可能会删除数十万行。

在(稍慢)测试服务器上运行它需要一个小时左右的时间,并从其他进程中读取/写入它来杀死该表。

我不介意它需要一段时间才能运行(虽然速度越快越好) - 但我不能让它在运行时锁定表一小时,因为有持续的读/写(主要写道。)

我的DB知识非常基础,因为我是编码员而不是dba。

有人可以以最有效的方式为我提供执行此任务的合适方法。

5 个答案:

答案 0 :(得分:6)

您正在寻找的是基于分区的滑动窗口:How to Implement an Automatic Sliding Window in a Partitioned Table on SQL Server 2005。每天对表进行分区,您可以在午夜的单个分区切换操作中有效地停止一整天。分区开关基本上是瞬时的。

如果你想要一个开销略低的解决方案(分区会产生严重的后果并影响整个应用程序,特别是当索引必须对齐时,这是快速切换操作的要求),那么你必须设计你的模式按照这个操作。置信度为99.99%,我可以说myTable 最左边的群集密钥必须Created字段。这将允许有效的批量删除(delete top (2500) from myTable where Created < ...)。您希望对其进行批处理有很多原因(一次最多2500个),最重要的是您必须避免锁定升级,并且必须将任何单个事务的大小保持在合理的限制范围内。

答案 1 :(得分:3)

您的方法将遭受与正常删除相同的疾病 - 您在[已创建]上没有索引。因此,您的方法更加复杂。

我建议您创建所述索引并在测试服务器上尝试正常删除。

另一个建议 - 通过调度程序在正常工作时间之外运行。

答案 2 :(得分:3)

要提高性能,您应该考虑在Created字段上创建索引,如果这是您想要经常做的事情。

然后你可以使用

DELETE FROM myTable 
WHERE Created < GETDATE()-30

我已经看到,通过适当的索引和统计信息添加,多小时进程减少到几秒钟。

索引很容易创建,并且可以使用工具来建议索引并提供语法。示例:MS SQL 2005 Management Studio中的SQL Tuning Advisor。

答案 3 :(得分:2)

我将假设您无法索引Created列(因为这是另一个开始的逻辑位置)。基于该假设,您将遇到性能和锁定问题。但是,由于您使用的是SQL 2005,因此可以利用本文中指定的一些新功能: http://nayyeri.net/reduce-locks-for-delete-and-update-commands-in-sql-server-2005-with-top-clause

基本上,创建一个选择您想要影响的所有记录的查询。将行标识符(已编制索引)写入临时表。根据标识符将临时表链接到要删除的表。然后使用此处指定的批量删除一次删除组。

通过这种方式,您可以根据日期条件创建临时表(由于非索引而无效,但您可以设置NOLOCK,这样就不会锁定您)。然后批量删除表,以减少对实际删除的锁定。

答案 4 :(得分:0)

创建索引并在办公时间之外执行删除可能是最好的事情。但是,如果它们不是选项,您可以根据查询创建视图并删除该视图,因此只需要引用该表一次,而不是两次,从而加快IO操作。

create view v1 as (select * FROM myTable WHERE Created < GETDATE()-30;)
delete from v1