6

我有一个 SQL Server 2005 数据库,我尝试在适当的字段上放置索引,以加快DELETE数百万行(big_table只有 3 列)表中记录的速度,但现在DELETE执行时间更长!(例如 1 小时对 13 分钟)

我在表之间有关系,我过滤的列DELETE在另一个表中。例如

DELETE FROM big_table
WHERE big_table.id_product IN (
SELECT small_table.id_product FROM small_table
WHERE small_table.id_category = 1)

顺便说一句,我也试过:

DELETE FROM big_table
WHERE EXISTS
(SELECT 1 FROM small_table
WHERE small_table.id_product = big_table.id_product
AND small_table.id_category = 1)

虽然它似乎比第一个运行得稍快,但使用索引仍然比没有索引慢很多。

我在这些字段上创建了索引:

  1. big_table.id_product
  2. small_table.id_product
  3. small_table.id_category

我的 .ldf 文件在DELETE.

DELETE当我的表上有索引时,为什么我的查询会变慢? 我以为他们应该跑得更快。

更新

好的,共识似乎是索引将大大减慢,DELETE因为必须更新索引。虽然,我仍然不明白为什么它不能DELETE一次所有的行,而只是在最后更新一次索引。

我从我的一些阅读中得到的印象是,DELETE通过更快地搜索WHERE子句中的字段,索引会加快速度。

Odetocode.com 说:

“在 DELETE 和 UPDATE 命令中搜索记录时,索引的工作方式与在 SELECT 语句中一样。”

但在文章后面,它说过多的索引会损害性能。

鲍勃问题的答案:

  1. 表中有 5500 万行
  2. 4200 万行被删除
  3. 类似SELECT的语句不会运行(抛出了“System.OutOfMemoryException”类型的异常)

我尝试了以下 2 个查询:

SELECT * FROM big_table
WHERE big_table.id_product IN (
SELECT small_table.id_product FROM small_table
WHERE small_table.id_category = 1)

SELECT * FROM big_table
INNER JOIN small_table
ON small_table.id_product = big_table.id_product
WHERE small_table.id_category = 1

运行25 分钟后两者均失败,并出现来自 SQL Server 2005 的以下错误消息:

An error occurred while executing batch. Error message is: Exception of type 'System.OutOfMemoryException' was thrown.

数据库服务器是具有 7.5 GB 内存的较旧的双核 Xeon 机器。这是我的玩具测试数据库:) 所以它没有运行其他任何东西。

我需要对我的索引做一些特殊的事情CREATE以使它们正常工作吗?

4

5 回答 5

29

索引使查找更快 - 就像一本书后面的索引。

更改数据的操作(如 DELETE)较慢,因为它们涉及操作索引。考虑本书后面的相同索引。如果您添加、删除或更改页面,您还有更多工作要做,因为您还必须更新索引。

于 2010-08-10T22:07:02.017 回答
3

我同意 Bobs 上面的评论 - 如果您要从大型表中删除大量数据,则删除索引可能需要一段时间才能删除数据,但这是开展业务的成本。当它删除所有数据时,您会导致重新索引事件发生。

关于日志文件的增长;如果你没有对你的日志文件做任何事情,你可以切换到简单日志;但我敦促您在更改之前阅读可能对您的 IT 部门产生的影响。

如果需要实时删除;直接在表上或另一个表中将数据标记为不活动并将该数据从查询中排除通常是一种很好的解决方法;然后稍后再回来并在用户不盯着沙漏时删除数据。涵盖这一点还有第二个原因;如果您要从表中删除大量数据(这是我根据您的日志文件问题所假设的),那么您可能需要执行 indexdefrag 来重新组织索引;如果您不喜欢手机上的用户,那么在工作时间之外这样做是可行的!

于 2010-08-11T06:25:53.047 回答
1

JohnB 正在删除大约 75% 的数据。我认为以下可能是一个可能的解决方案,并且可能是更快的解决方案之一。不要删除数据,而是创建一个新表并插入您需要保留的数据。插入数据后在该新表上创建索引。现在删除旧表并将新表重命名为与旧表相同的名称。

以上当然假设有足够的磁盘空间来临时存储重复的数据。

于 2013-03-25T08:58:18.050 回答
1

尝试这样的事情来避免批量删除(从而避免日志文件增长)

declare @continue bit = 1

-- delete all ids not between starting and ending ids
while @continue = 1
begin

    set @continue = 0

    delete top (10000) u
    from    <tablename> u WITH (READPAST)
    where   <condition>

    if @@ROWCOUNT > 0
        set @continue = 1 

end
于 2015-01-14T17:43:42.657 回答
0

您还可以尝试对 DELETE 语法进行 TSQL 扩展并检查它是否提高了性能:

DELETE FROM big_table
FROM big_table AS b
INNER JOIN small_table AS s ON (s.id_product = b.id_product)
WHERE s.id_category  =1
于 2010-08-10T22:27:19.847 回答