我正在执行巨大的DELETE FROM,而没有where子句。它基本上等效于TRUNCATE TABLE语句-除了不允许使用TRUNCATE。问题是表很大-1000万行,要花一个多小时才能完成。没有以下方法可以提高速度吗?
使用截断
禁用或删除索引?磁盘。
欢迎提出任何建议!
#1 楼
您可以做的是这样的批量删除:删除很高百分比的行...SELECT 'Starting' --sets @@ROWCOUNT
WHILE @@ROWCOUNT <> 0
DELETE TOP (xxx) MyTable
评论
@tuseau:每个删除都需要一些日志空间以防出错,以进行回滚。 50k行删除比10m行删除占用更少的资源/空间。当然,日志备份仍在运行,并且会占用空间,但是在服务器上进行小批量处理比处理大批备份更容易。
– gbn
2011-3-15 14:29
谢谢,批量删除有所帮助,我想这是最好的选择。
–图索
2011年3月16日在9:36
@Phil Helmer:如果批量删除是在事务中,则使用它是没有收益的。否则,每次写入的日志都较小,这就是简单的加载
– gbn
2011年4月21日在7:36
进一步说明:批量删除有很大帮助,从1小时42分钟到3分钟删除2000万行-但是请确保该表具有聚集索引!如果是堆,TOP子句会在执行计划中创建一个排序,该排序会否定任何改进。事后似乎很明显。
–图索
2011年4月21日在9:18
@Noumenon:确保@@ ROWCOUNT为1
– gbn
15年8月4日在7:45
#2 楼
您可以使用TOP子句轻松完成此操作:WHILE (1=1)
BEGIN
DELETE TOP(1000) FROM table
IF @@ROWCOUNT < 1 BREAK
END
评论
大括号格式化您的代码
– gbn
2011年3月15日15:10
@gbn就是这样。这里仍然是101 010。
–bernd_k
2011-3-15在19:12
#3 楼
如果您无法使用TRUNCATE,我同意将删除内容批量处理为可管理块的建议,并且我喜欢drop / create建议的独创性,但是我对您的问题中的以下评论感到好奇:它基本上等效于TRUNCATE TABLE语句-除非我不允许使用TRUNCATE
我猜想此限制的原因是与直接截断一个表所需的安全性以及它允许您截断您所关心的表以外的其他表这样的事实有关。
假设是这种情况,我想知道是否创建一个使用TRUNCATE TABLE并使用“ EXECUTE AS”的存储过程,而不是为直接截断该表所必需的安全权限提供一种可行的选择。
希望,这可以为您提供所需的速度,同时还可以通过将帐户添加到db_ddladmin角色来解决公司可能存在的安全问题。使用存储过程的这种方式是,可以将存储过程本身锁定,以便仅允许特定帐户使用它。需要删除该表中的数据是需要每天/每小时/等一次的操作,我将要求创建一个SQL Agent作业以每天在计划的时间截断该表。
希望这会有所帮助!
#4 楼
除截断外,..只有批量删除可以为您提供帮助。在Management Studio中,您可以选择脚本编写要删除和创建的表的脚本,因此它应该是一个简单的选项。但是,只有在允许您执行DDL操作的情况下,这才是真正的选择。评论
因为该应用程序是为并发操作而设计的,所以更改结构(DDL)和使用截断不是选项...我想批量删除是最好的选择。不过谢谢
–图索
2011-3-16在9:35
#5 楼
由于此问题是如此重要,因此我将发布此代码,该代码确实帮助我了解了使用循环删除以及在循环内进行消息传递以跟踪进度的方法。归功于@RLF作为查询基础。CREATE TABLE #DelTest (ID INT IDENTITY, name NVARCHAR(128)); -- Build the test table
INSERT INTO #DelTest (name) SELECT name FROM sys.objects; -- fill from system DB
SELECT COUNT(*) TableNamesContainingSys FROM #deltest WHERE name LIKE '%sys%'; -- check rowcount
go
DECLARE @HowMany INT;
DECLARE @RowsTouched INT;
DECLARE @TotalRowCount INT;
DECLARE @msg VARCHAR(100);
DECLARE @starttime DATETIME
DECLARE @currenttime DATETIME
SET @RowsTouched = 1; -- Needs to be >0 for loop to start
SET @TotalRowCount=0 -- Total rows deleted so far is 0
SET @HowMany = 5; -- Variable to choose how many rows to delete per loop
SET @starttime=GETDATE()
WHILE @RowsTouched > 0
BEGIN
DELETE TOP (@HowMany)
FROM #DelTest
WHERE name LIKE '%sys%';
SET @RowsTouched = @@ROWCOUNT; -- Rows deleted this loop
SET @TotalRowCount = @TotalRowCount+@RowsTouched; -- Increment Total rows deleted count
SET @currenttime = GETDATE();
SELECT @msg='Deleted ' + CONVERT(VARCHAR(9),@TotalRowCount) + ' Records. Runtime so far is '+CONVERT(VARCHAR(30),DATEDIFF(MILLISECOND,@starttime,@currenttime))+' milliseconds.'
RAISERROR(@msg, 0, 1) WITH NOWAIT; -- Print message after every loop. Can't use the PRINT function as SQL buffers output in loops.
END;
SELECT COUNT(*) TableNamesContainingSys FROM #DelTest WHERE name LIKE '%sys%'; -- Check row count after loop finish
DROP TABLE #DelTest;
评论
如果您将要做很多事情,请考虑对表进行分区您不能使用TRUNCATE,因为存在FK约束引用该表吗?