假设我们有30个列和500,000行的表Sales。我想删除表中的400,000(那些"toDelete='1'"
)。
但我有一些限制:
TRUNCATE
),但在做"DELETE ... WHERE..."
时(我需要设置一个条件),但还没有找到任何方法来做这个...任何建议都欢迎改变一个
DELETE FROM Sales WHERE toDelete='1'
更多分区和可能的事务日志免费。
调用DELETE FROM TableName
将在一个大型事务中执行整个删除操作。这很贵。
这是另一个批量删除行的选项:
deleteMore:
DELETE TOP(10000) Sales WHERE toDelete='1'
IF @@ROWCOUNT != 0
goto deleteMore
你想要的是批处理。
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。如果使用级联删除,则可能需要较小的数字,因为要删除这些子记录。
我过去必须执行此操作的一种方法是使用存储过程或脚本来删除n条记录。重复直到完成。
DELETE TOP 1000 FROM Sales WHERE toDelete='1'
你应该尝试给它一个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
我对此功能的看法如下。这样就没有重复的代码,你可以管理你的块大小。
DECLARE @DeleteChunk INT = 10000
DECLARE @rowcount INT = 1
WHILE @rowcount > 0
BEGIN
DELETE TOP (@DeleteChunk) FROM Sales WITH(ROWLOCK)
SELECT @rowcount = @@RowCount
END
我将在这里留下我的答案,因为我能够测试不同的批量删除和更新方法(我必须更新然后删除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 /* 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倍)。
我用下面的内容删除了大约5000万条记录 -
BEGIN TRANSACTION
DeleteOperation:
DELETE TOP (BatchSize)
FROM [database_name].[database_schema].[database_table]
IF @@ROWCOUNT > 0
GOTO DeleteOperation
COMMIT TRANSACTION
请注意,保持BatchSize <5000的资源更便宜。
我认为删除大量记录的最佳方法是通过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]
你可以在这里解释这种方法qazxsw poi