2009-12-17 88 views
14

我有一个大型SQL Server数据库,其中包含大约45万条记录的表。我将这张表格归档,并且需要删除大于两年前的所有条目。我有插入到我的档案表工作正常,但我有删除时的效率问题。SQL Server 2000删除顶部(1000)

我的问题在于目前在桌上的索引。我想在1000个记录块中删除(和存档插入)。为此,我需要确定满足要求(大于两年)的“最高”1000条记录。行上的DateTime标记是一个聚集索引,所以这对抓取行非常有用。但是SQL 2000不允许删除TOP 1000 ....所以我需要做的是这样的:

DELETE FROM <table> WHERE [UniqueID] IN 
(SELECT TOP 1000 [UniqueID] FROM <table> WHERE [DateTime] < @TwoYearsAgo) 

这将工作的伟大,如果UniqueID的被索引。由于不是这样,所以需要很长时间(它正在扫描要删除的1000个记录中的每个记录)。表格中没有唯一标识记录的其他索引。我被告知在UniqueID上计算索引的成本太高,因为这是一个实时数据库。任何人都可以指出一种方法来优化此查询?

+3

您是怎么设法添加一个名为UniqueID的列,它标识行,但没有索引?你没有主键吗? – 2009-12-17 23:10:34

+0

我没有设计表格,我是负责存档的项目的新程序员。这是一个繁重的写表(用于记录),但读取并不多,因此创建一个额外的索引(或PK,就此而言)会导致不必要的插入时间增加。这又不是我设计它的方式,而是它的原理。 – Kevin 2009-12-17 23:18:53

+3

那个告诉你索引太贵的人撒谎。 :-) – onupdatecascade 2009-12-17 23:19:46

回答

17

如何重写查询?在SET ROWCOUNT (Transact-SQL)

SET ROWCOUNT 1000 
DELETE FROM <table> WHERE [DateTime] < @TwoYearsAgo 

查看文档。

另请注意,根据DELETE的文档,它支持TOP子句,但对于SQL Server 2005及更高版本来说,这显然是新增功能。我这样说,因为它听起来像你的数据库服务器不支持,但你真的尝试过使用它吗?我无权访问SQL Server 2000文档,因此我不确定该版本是否受支持。这很可能不是。

DELETE TOP (1000) FROM <table> WHERE [DateTime] < @TwoYearsAgo 

注意从选择方式TOP的差别可以写,没有括号。对于UPDATE,DELETE和INSERT,表达式必须加上括号,即使它只是一个像上面那样的常数。

+0

我也在推动迁移到Server 2008,但我们很可能会在将数据库移动到新实例之前修剪数据库。 – Kevin 2009-12-17 23:21:31

+0

是的,我已经试过有和没有括号,都没有用。 – Kevin 2009-12-17 23:37:58

+0

请注意,根据MSDN文档,在SQL Server 2012之后的下一个版本中,SET ROWCOUNT不会影响插入,删除和更新语句。因此,如果您想要一个适用于所有版本的查询,您必须执行'从(选择顶部...)删除' – ChrisWue 2013-07-09 03:42:58

2

你可以使用SET ROWCOUNT

SET ROWCOUNT 1000 
DELETE FROM <table> WHERE [DateTime] < @TwoYearsAgo 
+0

我曾经在某处看到过这个建议,但我觉得这在现场数据库中很危险。我会更多地研究它,谢谢你的建议。 – Kevin 2009-12-17 23:20:51

3

你也可以做

DELETE TOP(1000) FROM <table> WHERE [DateTime] < @TwoYearsAgo 

只有上帝知道他们为什么使用顶部(X)用于删除和选择顶的X,大多数人不甚至似乎知道这个功能!

编辑:显然它的2005+,所以你应该可能忽略这一点。

1

我不得不做类似的事情 - 使轻量级插入和删除将旧记录移动到归档表。尽管违反直觉,我发现最快和最有影响力的解决方案是:

  1. 做一个小#TEMP表的ID为顶部(x)的行中的值。如果您的场景中的ID确实无法编入索引,则可以使用日期和ID,因此两者的组合可以使用索引。

  2. 开始TRAN

  3. 插入到归档表,其中ID和日期(#TEMP)

  4. 从主表中删除其中ID和日期(#TEMP)

  5. 提交

  6. 截断#temp

  7. 重复在

具有临时表阶段行标识符是比直删除更多的总的工作,但使进程在你只想蚕食一点点在一个时间不会阻塞情况非常轻巧。

另外我同意拉斯 - 无法看到一个唯一的ID没有指数,因此没有约束,强制执行它的观点。

+0

我尝试了一些类似于本地声明的临时表,但没有索引的唯一标识符,它没有多大帮助。我会尝试使用日期和唯一标识符,看看是否让我在任何地方。谢谢! – Kevin 2009-12-17 23:39:44

8

您可以删除一个子查询:

DELETE <table> FROM (
    SELECT TOP 1000 * 
    FROM <table> 
    WHERE [DateTime] < @TwoYearsAgo); 

见例如E:在SQL 2000 DELETE Syntax。这建议通过SET ROWCOUNT方法。在SQL 2005和更高版本中,您可以直接在DELETE中指定TOP。

0

我想知道您是否必须坚持1000条记录块要求。如果它是有服务器负载的原因和那种随心所欲,你可能想尝试以下,因为你已经有[DATETIME]一个聚集索引:

DELETE FROM <table> 
WHERE [DateTime] < @TwoYearsAgo 
and [DateTime] < (select dateadd(day, 1, min([DateTime])) from <table>) 
0

为了向后兼容,括号是可选在SELECT语句中。我们建议您始终在SELECT语句中对TOP使用括号,以便与要求在使用括号的INSERTUPDATEMERGEDELETE语句中的要求保持一致。

USE AdventureWorks; 
GO 
DELETE TOP (20) 
FROM Purchasing.PurchaseOrderDetail 
WHERE DueDate < '20120701'; 
GO