【问题标题】:Delete statement in SQL is very slowSQL中的delete语句很慢
【发布时间】:2012-06-05 16:39:34
【问题描述】:

我有这样的语句超时:

DELETE FROM [table] WHERE [COL] IN ( '1', '2', '6', '12', '24', '7', '3', '5')

我试着像这样一次做一个:

DELETE FROM [table] WHERE [COL] IN ( '1' )

到目前为止,它是 22 分钟,并且还在继续。

该表有 260,000 行,四列。

有没有人知道为什么会这么慢以及如何加快速度? 我在 [COL] 上确实有一个非唯一的非聚集索引,我正在执行 WHERE。 我正在使用 SQL Server 2008 R2

更新:我的桌子上没有触发器。

【问题讨论】:

  • 如果你这样做where [col] = '1'会发生什么?
  • 你检查过查询的执行计划是否有任何显示吗?
  • DELETE 语句由于日志的原因通常很慢。 TRUNCATE 更快。但在这种情况下您不能使用 TRUNCATE。我没有任何进一步的线索
  • 一定有触发器或者死锁的东西。

标签: sql sql-server sql-server-2008 database-optimization


【解决方案1】:

可能导致删除速度变慢的原因:

  • 删除大量记录
  • 许多索引
  • 子表中的外键索引缺失。 (感谢 @CesarAlvaradoDiaz 在 cmets 中提到这一点)
  • 死锁和阻塞
  • 触发器
  • 级联删除(您要删除的那十条父记录可能意味着 数百万条子记录被删除)
  • 需要增长的事务日志
  • 要检查的许多外键

因此,您的选择是找出阻塞并修复它,或者在不影响正常生产负载的非工作时间运行删除。您可以批量运行删除(如果您有触发器、级联删除或大量记录,则很有用)。您可以删除并重新创建索引(最好在下班时间也可以这样做)。

【讨论】:

  • 当我试图从包含约 100 万行的表中删除时,我遇到了一个案例,但它花了很长时间。使用select * 查询行很快,但删除速度非常慢。然后我意识到另一个表有一个外键,它有 20 亿(!)行。当然,FK 列没有被索引。解决方案:删除 FK,删除行,重新创建 FK。重新创建 FK 仍然需要一些时间,但速度要快得多。
  • 重要提示:如果您的表中有外键,则应将这些外键编入索引
  • 从表中删除大量记录列表时禁用触发器是否是一种好习惯?
  • @Aviator,仅当您的删除脚本处理触发器处理的内容时。否则你很可能会导致大规模的数据完整性问题。
  • 关于外键问题的详细信息,因为我一开始错过了。不要只考虑该表对其他人的外键约束。想想其他表在这个表上的外键。因此,如果您运行\d my table,请查看Referenced by。我的问题是我有一个巨大的表,它引用了我试图删除的表,而另一个表正在使用的列需要一个索引。
【解决方案2】:
  1. 禁用约束

    ALTER TABLE [TableName] NOCHECK CONSTRAINT ALL;

  2. 禁用索引

    ALTER INDEX ALL ON [TableName] DISABLE;

  3. 重建索引

    ALTER INDEX ALL ON [TableName] REBUILD;

  4. 启用约束

    ALTER TABLE [TableName] CHECK CONSTRAINT ALL;

  5. 再次删除

【讨论】:

  • 这行得通,但解释一下就好了。
  • 一个缺点是在alter index all on mytable disable 之后大多数查询停止工作。 “查询处理器无法生成计划”。一旦重建索引并重新启用约束,它们似乎又可以工作了。
  • 当禁用表上的索引时,所有指向表的外键也将被禁用。这可能是加速的解释。之后,您可以select referrer, fk from foreign_keys where is_disabled = 1,或查看禁用索引时产生的警告消息。然后对于每个受影响的子表,alter table mychild with check check constraint all。这可能会发现一些现在需要删除的子行!
  • 你也可能会得到标记为“不可信”的 FK,所以select referrer, fk from foreign_keys where is_not_trusted = 1 并修复这些问题。
  • 警告:在大表(200 万+)中重建索引需要很多时间(小时...)。在我的情况下,最好按照@Andomar的回答做。
【解决方案3】:

删除大量行可能会非常慢。尝试一次删除一些,例如:

delete top (10) YourTable where col in ('1','2','3','4')
while @@rowcount > 0
    begin
    delete top (10) YourTable where col in ('1','2','3','4')
    end

【讨论】:

  • 这确实有效!我唯一的解释是它不需要写一个大的日志。如果您有任何其他解释,请与我分享。
【解决方案4】:

就我而言,数据库统计信息已损坏。声明

delete from tablename where col1 = 'v1' 

即使没有匹配的记录也需要 30 秒,但是

delete from tablename where col1 = 'rubbish'

立即跑

运行

update statistics tablename

修复了问题

【讨论】:

  • 也为我工作!
【解决方案5】:

预防措施

SQL Profiler 的帮助下检查此问题的根本原因。可能有Triggers 导致执行延迟。它可以是任何东西。不要忘记在启动Trace 时选择Database NameObject Name 以排除扫描不必要的查询...

Database Name Filtering

Table/Stored Procedure/Trigger Name Filtering

纠正措施

正如您所说,您的表包含 260,000 条记录...IN Predicate 包含六个值。现在,对于 IN Predicate 中的每个值,每条记录都被搜索 260,000 次。相反,它应该是像下面这样的内部联接...

Delete K From YourTable1 K
Inner Join YourTable2 T on T.id = K.id

IN Predicate 值插入Temporary TableLocal Variable

【讨论】:

    【解决方案6】:

    如果您要从中删除的表具有 BEFORE/AFTER DELETE 触发器,则其中的某些内容可能会导致您的延迟。

    此外,如果您有引用该表的外键,则可能会发生额外的 UPDATE 或 DELETE。

    【讨论】:

      【解决方案7】:

      其他表可能对您的 [表] 有 FK 约束。 因此数据库需要检查这些表以保持引用完整性。 即使您拥有与这些 FK 对应的所有所需索引,也要检查它们的数量。

      当 NHibernate 错误地在同一列上创建了重复的 FKs,但名称不同(SQL Server 允许这样做)时,我遇到了这种情况。 它大大减慢了 DELETE 语句的运行速度。

      【讨论】:

      【解决方案8】:

      检查此删除语句的执行计划。看看是否使用了索引搜索。还有col的数据类型是什么?

      如果您使用了错误的数据类型,请更改更新语句(例如从 '1' 到 1 或 N'1')。

      如果使用索引扫描,请考虑使用一些query hint..

      【讨论】:

      • 就我而言,缺少索引。添加索引解决了这个问题。
      【解决方案9】:

      [COL] 真的是一个包含数字的字符字段,还是可以去掉值周围的单引号? @Alex 说得对,IN 比 = 慢,所以如果你能做到这一点,你会过得更好:

      DELETE FROM [table] WHERE [COL] = '1'
      

      但最好还是使用数字而不是字符串来查找行(sql 喜欢数字):

       DELETE FROM [table] WHERE [COL] = 1
      

      不妨试试:

       DELETE FROM [table] WHERE CAST([COL] AS INT) = 1
      

      在任何一种情况下,请确保您在列 [COL] 上有一个索引以加快表扫描。

      【讨论】:

      • 不幸的是,该列包含字母和数字,我确实有一个索引 =/。
      • 我不确定它是否有帮助,但我们在这里做的一件事是在我们所有重要的表中添加一个“IsActive”列,允许我们更新字段而不是删除行。便于审计并且在删除时重建索引方面不那么混乱。当然,所有后续的视图/查询/过程/函数都必须包含“WHERE ISACTIVE = 1”。
      • 将数据库中的所有值转换为 int 实际上可能会严重影响性能。它也可能会阻止 sql server 可以使用索引。因此,如果您的提案有任何效果,那很可能会更糟。
      【解决方案10】:

      我阅读了这篇文章,它对解决任何类型的不便非常有帮助

      https://support.microsoft.com/en-us/kb/224453

      这是一个waitresource的例子 密钥:16:72057595075231744 (ab74b4daaf17)

      -- First SQL Provider to find the SPID (Session ID)
      
      -- Second Identify problem, check Status, Open_tran, Lastwaittype, waittype, and waittime
      -- iMPORTANT Waitresource select * from sys.sysprocesses where spid = 57
      
      select * from sys.databases where database_id=16
      
      -- with Waitresource check this to obtain object id 
      select * from sys.partitions where hobt_id=72057595075231744
      
      select * from sys.objects where object_id=2105058535
      

      【讨论】:

        【解决方案11】:

        如果您要删除表中的所有记录,而不是选择少数记录,则删除并重新创建表可能会快得多。

        【讨论】:

          【解决方案12】:

          检查了一个 SSIS 包(由于 SQL Server 执行命令非常慢),这是在我写这篇文章之前大约 5-4 年在我们的客户端中设置的,我发现有以下内容任务: 1) 将 XML 文件中的数据插入到名为 [Importbarcdes] 的表中。

          2) 在另一个目标表上合并命令,使用上述表作为源。

          3) "delete from [Importbarcodes]",清除SSIS包的任务读取XML文件后插入的行的表。

          在快速检查表 ImportBarcodes 上只有 1 行的所有语句(SELECT、UPDATE、DELETE 等)后,执行大约需要 2 分钟。

          扩展事件显示了大量 PAGEIOLATCH_EX 等待通知。

          表中没有索引,也没有注册触发器。

          仔细检查表的属性,在“存储”选项卡和“常规”部分下,“数据空间”字段显示在页面中分配的空间超过 6 GIGABYTES。

          发生了什么:

          在过去的 4 年中,查询每天都会运行大部分时间,在表中插入和删除数据,将未使用的页面文件留在后面而不释放它们。

          所以,这就是扩展事件会话捕获的等待事件和表上缓慢执行的命令的主要原因。

          运行ALTER TABLE ImportBarcodes REBUILD 修复了释放所有未使用空间的问题。 TRUNCATE TABLE ImportBarcodes 做了类似的事情,唯一的区别是删除所有页面文件和数据。

          【讨论】:

            【解决方案13】:

            较旧的主题,但仍然相关。 当索引变得碎片化到成为问题而不是帮助的程度时,就会出现另一个问题。在这种情况下,答案是重建或删除并重新创建索引并再次发出删除语句。

            【讨论】:

              【解决方案14】:

              作为上述Andomar's answer 的扩展,我有一个场景,前 700,000,000 条记录(约 12 亿条)处理速度非常快,每秒处理 25,000 条记录的块(大约)。但是,然后它开始需要 15 分钟才能完成 25,000 个批次。我将块大小减少到 5,000 条记录,然后它又恢复了以前的速度。我不确定我达到了什么内部阈值,但修复是减少记录数,进一步恢复速度。

              【讨论】:

                【解决方案15】:

                打开 CMD 并运行此命令

                NET STOP MSSQLSERVER
                NET START MSSQLSERVER
                

                这将重新启动 SQL Server 实例。 尝试在您的删除命令后再次运行

                我在批处理脚本中有这个命令,如果遇到这样的问题,我会不时运行它。正常的 PC 重启会有所不同,因此如果您的 sql server 遇到一些问题,重启实例是最有效的方法。

                【讨论】:

                • 这不能回答最初的问题,或者如果回答了,你没有解释为什么这会有所帮助。
                • 这不是一个解决方案,尤其是在生产环境中,这是一种不应该做的事情。另外这将如何提高删除速度?
                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2015-09-21
                • 2015-12-14
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多