【问题标题】:SQL Server DELETE is slower with indexesSQL Server DELETE 使用索引较慢
【发布时间】:2011-03-28 02:22:36
【问题描述】:

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

我在 to 表之间有关系,而我过滤 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 says:

“在 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 之后,我是否需要对我的索引做一些特别的事情以使它们正常工作?

【问题讨论】:

  • 表格有多少行?有多少行被删除?完成类似的 SELECT 语句需要多长时间?了解 SELECT 语句的执行速度可能有助于了解索引如何影响 DELETE。
  • 这需要更长的时间,因为当您执行删除时,引用您的表的索引也必须更新。
  • 5500 万行,42 行已删除,未完成,详情见上文

标签: sql sql-server subquery sql-delete


【解决方案1】:

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

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

【讨论】:

    【解决方案2】:

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

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

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

    【讨论】:

      【解决方案3】:

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

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

      【讨论】:

        【解决方案4】:

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

        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
        

        【讨论】:

        • 这个问题似乎更多是关于为什么索引很慢而不是如何删除大量条目,但这似乎很有用,我发现这个问题是因为我需要删除很多参赛作品,谢谢!
        【解决方案5】:

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

        【讨论】:

        • 这根本没有帮助;与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) 花费的时间完全相同
        猜你喜欢
        • 2011-01-05
        • 1970-01-01
        • 2010-12-04
        • 2021-04-19
        • 2011-05-24
        • 2014-06-26
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多