SQL Server DELETE的索引速度较慢

时间:2010-08-10 22:04:10

标签: sql sql-server subquery sql-delete

我有一个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
  4. 我的.ldf文件在DELETE期间增长很多。

    当我的表上有索引时,为什么我的DELETE查询会变慢?我认为它们应该运行得更快。

    更新

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

    我的一些阅读表明,通过更快地搜索DELETE子句中的字段,索引会加快WHERE

    Odetocode.com says:

    “在DELETE和UPDATE命令中搜索记录时,索引也可以像在SELECT语句中一样工作。”

    但是在文章后面,它说太多的索引会影响性能。

    回答 bobs 问题:

    1. 表格中的5500万行
    2. 正在删除4200万行
    3. 类似的SELECT语句不会运行(抛出类型'System.OutOfMemoryException'的异常)
    4. 我尝试了以下两个查询:

      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.
      

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

      在我CREATE之后我是否需要对我的索引做一些特别的事情才能让它们正常工作?

5 个答案:

答案 0 :(得分:27)

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

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

答案 1 :(得分:2)

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

关于日志文件的增长;如果你没有对你的日志文件做任何事情,你可以切换到Simple日志记录;但我建议您在更改之前了解可能对您的IT部门产生的影响。

如果您需要实时删除;它通常是一个很好的工作,可以直接在表或另一个表中将数据标记为非活动状态,并从查询中排除该数据;然后再回来并在用户不盯着沙漏时删除数据。覆盖这个的第二个原因;如果要从表中删除大量数据(这是我根据您的日志文件问题所假设的),那么您可能希望使用indexdefrag来重新生成索引;如果您不喜欢手机上的用户,那就是在非工作时间内完成这项工作!

答案 2 :(得分:1)

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

上述当然假设有足够的磁盘空间可用于临时存储重复数据。

答案 3 :(得分:0)

您还可以尝试使用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

答案 4 :(得分:0)

尝试这样的操作以避免批量删除(从而避免日志文件增长)

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