15

我有一个包含大约 4500 万条记录的表的大型 SQL Server 数据库。我正在归档此表,并且需要删除两年前的所有条目。我可以正常插入存档表,但是在删除时遇到了效率问题。

我的问题在于当前表上的索引。我想在 1000 个记录块中删除(和存档插入)。为此,我需要确定满足要求的“前”1000 条记录(超过两年)。该行上的 DateTime 标记是一个聚集索引,因此非常适合抓取行。但是 SQL 2000 不允许 DELETE TOP 1000 .... 所以我需要做类似的事情:

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

如果对 UniqueID 进行索引,这将非常有用。由于不是,这需要很长时间(它正在扫描表中要删除的 1000 条记录中的每一条)。表上没有其他唯一标识记录的索引。有人告诉我,在 UniqueID 上计算索引成本太高,因为这是一个实时数据库。谁能指出优化此查询的方法?

4

7 回答 7

19

重写查询怎么样?

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

请参阅有关SET ROWCOUNT (Transact-SQL) 的文档。

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

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

注意与 TOP on select 的写法不同不带括号。对于 UPDATE、DELETE 和 INSERT,表达式必须用括号括起来,即使它只是像上面那样的常数。

于 2009-12-17T23:09:31.113 回答
8

您可以删除子查询:

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

请参阅示例 E:在SQL 2000 DELETE Syntax中。建议使用 SET ROWCOUNT 方法。在 SQL 2005 及更高版本中,您可以直接在 DELETE 中指定 TOP。

于 2009-12-18T00:04:47.160 回答
4

你也可以

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

上帝只知道为什么他们使用 top(x) 进行删除和使用 top x 进行选择,大多数人似乎甚至不知道这个功能!

编辑:显然它的 2005+ 所以你应该忽略这个。

于 2009-12-17T23:21:57.777 回答
2

不久前我不得不做一些类似的事情——进行轻量级的插入和删除以将旧记录移动到存档表中。尽管违反直觉,但我发现的最快且影响最小的解决方案是:

  1. 制作一个小的 #temp 表,其中包含顶部 (x) 行的 ID 值。如果在您的场景中确实无法对 ID 进行索引,则可以使用 date AND ID 代替,因此两者的组合可以使用索引。

  2. 开始翻译

  3. 插入到 ID 和 DATE 在 (#temp) 中的存档表中

  4. 从 ID 和 DATE 在 (#temp) 中的主表中删除

  5. 犯罪

  6. 截断#temp

  7. 重复

使用临时表来暂存行标识符比直接删除要完成更多的工作,但是在您希望一次只删除一点而不阻塞的情况下,该过程非常轻量级。

我也同意 Lasse - 看不到没有索引的唯一 ID 的意义,因此没有约束来强制执行它。

于 2009-12-17T23:28:41.403 回答
2

您可以使用SET ROWCOUNT

SET ROWCOUNT 1000
DELETE FROM <table> WHERE [DateTime] < @TwoYearsAgo
于 2009-12-17T23:13:42.930 回答
0

我想知道您是否必须坚持 1000 条记录块的要求。如果由于服务器负载和任意类型的原因而存在,您可能需要尝试以下操作,因为您已经在 [DateTime] 上有一个聚集索引:

DELETE FROM <table> 
WHERE [DateTime] < @TwoYearsAgo 
and [DateTime] < (select dateadd(day, 1, min([DateTime])) from <table>)
于 2009-12-18T17:36:47.987 回答
0

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

USE AdventureWorks;
GO
DELETE TOP (20) 
FROM Purchasing.PurchaseOrderDetail
WHERE DueDate < '20120701';
GO
于 2013-04-08T06:11:13.040 回答