33

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

但我有一些限制:

  • 该表是“经常”读/写的,我不希望长时间的“删除”花费很长时间并将表锁定太久
  • 我需要跳过事务日志(例如使用 a TRUNCATE),但是在执行 a 时"DELETE ... WHERE..."(我需要设置一个条件),但还没有找到任何方法来做到这一点......

欢迎任何建议来改变

DELETE FROM Sales WHERE toDelete='1'

到更分区且可能没有事务日志的东西。

4

10 回答 10

47

调用DELETE FROM TableName将在一个大事务中完成整个删除。这是昂贵的。

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

deleteMore:
DELETE TOP(10000) Sales WHERE toDelete='1'
IF @@ROWCOUNT != 0
    goto deleteMore
于 2012-06-27T15:55:40.627 回答
18

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

TL;DR:始终按主键更新/删除,从不按任何其他条件。如果您不能直接使用 PK,请创建一个临时表并用 PK 值填充它,然后使用该表更新/删除您的表。为此使用索引。

我从上面的解决方案开始(@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 /* Id is the PK */
  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 倍)。

于 2019-03-06T10:39:26.320 回答
11

你想要的是批处理。

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。如果您使用级联删除,您可能需要一个较小的数字,因为您需要删除这些子记录。

于 2012-06-27T15:56:04.253 回答
5

过去我不得不这样做的一种方法是拥有一个删除n条记录的存储过程或脚本。重复直到完成。

DELETE TOP 1000 FROM Sales WHERE toDelete='1'
于 2012-06-27T15:52:32.310 回答
3

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

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

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

最终,您可以尝试遍历表并分块删除。

更新

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

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
于 2012-06-27T15:53:50.153 回答
3

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

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

WHILE @rowcount > 0
BEGIN

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

  SELECT @rowcount = @@RowCount
END
于 2016-11-23T14:37:07.133 回答
1

我已经使用以下内容删除了大约 5000 万条记录 -

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

     IF @@ROWCOUNT > 0
     GOTO DeleteOperation
COMMIT TRANSACTION

请注意,保持BatchSize < 5000 在资源上的成本更低。

于 2017-01-09T05:47:58.850 回答
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
于 2021-03-10T11:39:03.147 回答
0

我认为删除大量记录的最佳方法是将其删除Primary Key。(Primary Key 这里看到了什么)

因此,您必须生成包含要删除的整个行列表的 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/

于 2017-08-30T08:29:04.577 回答
-1
Declare @counter INT  
Set @counter = 10 -- (you can always obtain the number of rows to be deleted and set the counter to that value)  
While @Counter > 0  
Begin  
Delete TOP (4000) from <Tablename> where ID in (Select ID from <sametablename> with (NOLOCK) where DateField < '2021-01-04') -- or opt for GetDate() -1  
Set @Counter = @Counter -1 -- or set @counter = @counter - 4000 if you know number of rows to be deleted.  
End
于 2021-09-13T15:26:34.750 回答