SQL中的Delete语句非常慢


77

我有这样的语句正在超时:

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

我尝试一次像这样做一次:

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

到目前为止,已经到了22分钟,而且还在继续。

该表具有260,000行,为四列。

有谁知道为什么会这么慢以及如何加快速度?我在[WOL]上确实有一个非唯一,非聚集的索引,我正在其中进行操作。我正在使用SQL Server 2008 R2

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


1
如果这样做会where [col] = '1'怎样?
Alex Gitelman 2012年

3
您是否检查了查询的执行计划以查看其是否显示任何内容?
塔琳

3
由于存在日志,DELETE语句通常很慢。TRUNCATE更快。但是在这种情况下您不能使用TRUNCATE。我没有任何另外的线索
codingbiz


必须有一个触发器或某些死锁。
Sergey Kalinichenko 2012年

Answers:


88

可能导致删除速度变慢的事情:

  • 删除很多记录
  • 许多索引
  • 子表中的外键缺少索引。(感谢@CesarAlvaradoDiaz在评论中提及此内容)
  • 死锁和阻塞
  • 触发器
  • 级联删除(要删除的十个父记录可能意味着数百万个子记录被删除)
  • 交易日志需要增长
  • 许多外键要检查

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


28
我遇到了一种情况,当我试图从包含约100万行的表中删除时,却花了很长时间。查询行select *很快,但是删除却异常缓慢。然后我意识到从另一个有20亿(!)行的表中有一个外键。当然,FK列未建立索引。解决方案:删除FK,删除行,重新创建FK。重建FK仍然需要一些时间,但是速度要快得多。
Daniel Dinnyes 2014年

5
重要提示:如果您有外键,则表中的外键应编入索引
Cesar Alvarado Diaz

从表中删除庞大的记录列表时禁用触发器是一种好习惯吗?
aagjalpankaj

1
@Aviator,仅当您的删除脚本处理触发器所处理的内容时。否则,您可能会导致严重的数据完整性问题。
HLGEM '18年

64
  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. 再次删除


15
这行得通,但是一个解释会很好。
cdonner '16

2
缺点之一是alter index all on mytable disable大多数查询停止工作之后。“查询处理器无法制定计划”。重建索引并重新启用约束后,它们似乎可以再次工作。
Ed Avis

禁用表上的索引时,指向表的所有外键也将被禁用。这可能是加速的原因。之后,您可以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也要对其进行修复。
Ed Avis

2
警告:重建BIG表(超过200万个)中的索引需要花费大量时间(小时...)。就我而言,最好像@Andomar的回答中那样做。
マルちゃん

21

删除很多行可能很慢。尝试一次删除一些,例如:

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

预防措施

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

数据库名称过滤

表/存储过程/触发器名称过滤

纠正措施

如您所说,表包含260,000条记录...并IN Predicate包含六个值。现在,正在为的每个值搜索260,000次每个记录IN Predicate。相反,它应该是如下所示的内部联接...

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

IN Predicate值插入Temporary TableLocal Variable


3

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

此外,如果您有外键引用该表,则可能正在发生其他UPDATE或DELETE。


3

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

delete from tablename where col1 = 'v1' 

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

delete from tablename where col1 = 'rubbish'

立刻跑了

跑步

update statistics tablename

解决了这个问题


2

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

我遇到的情况是NHibernate在同一列上错误地创建了重复的FK,但是名称不同(SQL Server允许)。它极大地减慢了DELETE语句的运行。


1
这应该是评论,而不是答案。有了更多代表,您就可以发表评论
IKavanagh 2015年

0

[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]列上都有索引以加快表扫描的速度。


不幸的是,该列包含字母和数字,我确实有一个索引= /。
凯尔2012年

我不确定是否会有所帮助,但是我们在这里要做的一件事是在所有重要表中放入“ IsActive”列,使我们可以更新字段而不是删除行。便于审核,在删除索引时重建索引方面也比较麻烦。当然,所有后续视图/查询/处理/功能必须包含“ WHERE ISACTIVE = 1”。
罗素·福克斯

2
将数据库中的所有值强制转换为int可能实际上会严重损害性能。这也可能会阻止sql server使用索引。因此,如果您的建议有任何效果,那很可能会更糟。
Stefan Steinegger 2013年

0

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

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

如果使用索引扫描,请考虑使用一些查询提示..


0

我阅读了这篇文章,对于解决任何类型的不便确实很有帮助

https://support.microsoft.com/zh-CN/kb/224453

这是一个waitresource的案例KEY: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

0

如果要删除表中的所有记录,而不是删除一些记录,那么删除并重新创建表可能会更快。


0

在检查了一个SSIS程序包(由于SQL Server执行命令的速度非常慢)之后,在我撰写本文之前大约5至4年,在我们客户中建立了该程序包,我发现有以下任务:1 )将XML文件中的数据插入名为[Importbarcdes]的表中。

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

3)“从[导入条形码]删除”,以清除SSIS包任务读取XML文件后插入的行的表。

快速检查后,表ImportBarcodes上只有1行的所有语句(SELECT,UPDATE,DELETE等)花费了大约2分钟的时间来执行。

扩展事件显示了很多PAGEIOLATCH_EX等待通知。

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

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

发生了什么:

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

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

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


0

较旧的话题,但仍然有意义。当索引变得零散到某种程度而不仅仅是帮助时,就会出现另一个问题。在这种情况下,答案将是重建或删除并重新创建索引,然后再次发出delete语句。


0

作为上述Andomar答案的扩展,我遇到了这样一种情况:前700,000,000条记录(约12亿条)的处理速度非常快,每秒(大约)处理25,000条记录。但是,开始需要15分钟才能完成25,000个批次。我将块大小减小到5,000条记录,并恢复到以前的速度。我不确定我达到了什么内部阈值,但解决方法是减少记录数量,进一步恢复速度。


-7

打开CMD并运行此命令

NET STOP MSSQLSERVER
NET START MSSQLSERVER

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

我在批处理脚本中有此命令,如果遇到此类问题,会不时运行它。正常的PC重新启动将是不同的,因此,如果您遇到sql服务器问题,则重新启动实例是最有效的方法。


这不能回答原始问题,或者如果可以,您没有解释为什么这样做会有帮助。
mjuarez
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.