如何在500,000+行表中不使用截断表时有效删除行

时间:2012-06-27 15:48:24

标签: sql sql-server-2008 tsql sql-delete truncate

我们假设我们有30个列和500,000行的表 Sales 。我想在表格中删除400,000("toDelete='1'")。

但我有一些限制:

  • 表格被读/写"经常"而且我不想长时间删除"需要很长时间并将桌子锁定太久
  • 我需要跳过事务日志(比如使用TRUNCATE)但是在执行"DELETE ... WHERE..."时(我需要设置一个条件),但是没有找到任何方法来执行此操作...

欢迎任何建议改变

DELETE FROM Sales WHERE toDelete='1'

更多分区&可能事务日志自由。

9 个答案:

答案 0 :(得分:33)

调用DELETE FROM TableName将在一个大型事务中执行整个删除操作。这很贵。

这是另一个批量删除行的选项:

deleteMore:
DELETE TOP(10000) Sales WHERE toDelete='1'
IF @@ROWCOUNT != 0
    goto deleteMore

答案 1 :(得分:10)

您想要的是批处理。

While (select Count(*) from sales where toDelete =1) >0
BEGIN
Delete from sales where SalesID in
(select top 1000 salesId from sales where toDelete = 1)
END

当然你可以试验哪个是批次使用的最佳值,我根据表使用了500 - 50000。如果使用级联删除,则可能需要较小的数字,因为要删除这些子记录。

答案 2 :(得分:5)

过去我必须做的一件事就是拥有一个删除 n 记录的存储过程或脚本。重复直到完成。

DELETE TOP 1000 FROM Sales WHERE toDelete='1'

答案 3 :(得分:3)

您应该尝试给它一个ROWLOCK提示,这样它就不会锁定整个表格。但是,如果删除很多行,则会发生锁定升级。

此外,请确保toDelete列上有非群集过滤索引(仅适用于1个值)。如果可能的话,使它成为一个列,而不是varchar(或它现在是什么)。

DELETE FROM Sales WITH(ROWLOCK) WHERE toDelete='1'

最终,您可以尝试迭代表并删除块。

<强>更新

由于while循环和chunk删除是这里的新粉红色,我也会投入我的版本(结合我以前的答案):

SET ROWCOUNT 100
DELETE FROM Sales WITH(ROWLOCK) WHERE toDelete='1'

WHILE @@rowcount > 0
BEGIN
  SET ROWCOUNT 100
  DELETE FROM Sales WITH(ROWLOCK) WHERE toDelete='1'  
END

答案 4 :(得分:3)

我对此功能的看法如下。 这样就没有重复的代码,你可以管理你的块大小。

DECLARE @DeleteChunk INT = 10000
DECLARE @rowcount INT = 1

WHILE @rowcount > 0
BEGIN

  DELETE TOP (@DeleteChunk) FROM Sales WITH(ROWLOCK)

  SELECT @rowcount = @@RowCount
END

答案 5 :(得分:1)

我使用下面的内容删除了大约5000万条记录 -

BEGIN TRANSACTION     
     DeleteOperation:
     DELETE TOP (BatchSize)
     FROM  [database_name].[database_schema].[database_table] 

     IF @@ROWCOUNT > 0
     GOTO DeleteOperation
COMMIT TRANSACTION

请注意保留 BatchSize &lt; 5000资源更便宜。

答案 6 :(得分:1)

我将在这里留下答案,因为我能够测试用于批量删除和更新的不同方法(我必须先更新然后删除125 + mio行,服务器具有16GB RAM,Xeon E5-2680 @ 2.7GHz ,SQL Server 2012)。

TL; DR :始终通过主键进行更新/删除。如果您不能直接使用PK,请创建一个临时表并用PK值填充它,然后使用该表更新/删除表。为此使用索引。

我从above(@ Kevin Aenmey撰写)开始着手解决方案,但是这种方法被证明是不合适的,因为我的数据库处于活动状态,并且每秒处理数百个事务,并且涉及一些阻塞(使用WITH(ROWLOCK)并没有更改条件中所有字段的索引)。

因此,我添加了一个WAITFOR语句,该语句允许数据库处理其他事务。

deleteMore:
WAITFOR DELAY '00:00:01'
DELETE TOP(1000) FROM MyTable WHERE Column1 = @Criteria1 AND Column2 = @Criteria2 AND Column3 = @Criteria3
IF @@ROWCOUNT != 0
    goto deleteMore

此方法能够每小时处理约1.6mio行,而删除则处理约0.2mio行。

转到临时表会大大改变事情。

deleteMore:
SELECT TOP 10000 Id 
  INTO #Temp 
  FROM MyTable WHERE Column1 = @Criteria1 AND Column2 = @Criteria2 AND Column3 = @Criteria3 

DELETE MT
  FROM MyTable MT
  JOIN #Temp T ON T.Id = MT.Id 

/* you can use IN operator, it doesn't change anything
 DELETE FROM MyTable WHERE Id IN (SELECT Id FROM #Temp)

 */
IF @@ROWCOUNT > 0 BEGIN
    DROP TABLE #Temp
    WAITFOR DELAY '00:00:01'
    goto deleteMore
END ELSE BEGIN
    DROP TABLE #Temp
    PRINT 'This is the end, my friend'
END

此解决方案每小时处理〜25mio行(更新速度提高15倍),每小时处理2.2mio行(删除速度提高11倍)。

答案 7 :(得分:0)

我认为删除大量记录的最佳方法是Primary Key删除它。 (什么是Primary Key see here

因此,您必须生成包含要删除的整个行列表的tsql脚本,然后执行此脚本。

例如,下面的代码将生成该文件

GO
SET NOCOUNT ON

SELECT   'DELETE FROM  DATA_ACTION WHERE ID = ' + CAST(ID AS VARCHAR(50)) + ';' + CHAR(13) + CHAR(10) + 'GO'
FROM    DATA_ACTION
WHERE  YEAR(AtTime) = 2014

输出文件会有像

这样的记录
DELETE FROM  DATA_ACTION WHERE ID = 123;
GO
DELETE FROM  DATA_ACTION WHERE ID = 124;
GO
DELETE FROM  DATA_ACTION WHERE ID = 125;
GO

现在您必须使用SQLCMD实用程序才能执行此脚本。

sqlcmd -S [Instance Name] -E -d [Database] -i [Script]

你可以在这里解释这种方法https://www.mssqltips.com/sqlservertip/3566/deleting-historical-data-from-a-large-highly-concurrent-sql-server-database-table/

答案 8 :(得分:0)

当我知道大约有多少迭代时,我是这样做的:

delete from Activities with(rowlock) where Id in (select top 999 Id from Activities 
(nolock) where description like 'financial data update date%' and len(description) = 87 
and User_Id = 2);
waitfor delay '00:00:02'
GO 20

编辑:这对我来说比选择顶部效果更好更快:

declare @counter int = 1
declare @msg varchar(max)
declare @batch int = 499

while ( @counter <= 37600)

begin
    set @msg = ('Iteration count = ' + convert(varchar,@counter))
    raiserror(@msg,0,1) with nowait
    delete Activities with (rowlock) where Id in (select Id from Activities (nolock) where description like 'financial data update date%' and len(description) = 87 and User_Id = 2 order by Id asc offset 1 ROWS fetch next @batch rows only)
    set @counter = @counter + 1
    waitfor delay '00:00:02'
end