我想从 1TB 表中删除 10GB (1%) 数据。我读过几篇关于从大表中删除大量数据的文章,但没有找到太多关于从大表中删除较小比例数据的文章。
其他详细信息: 尝试从访问表中删除机器人数据。过滤条件是字段的组合... ip in(大约 20 个 ip 列表)和 useragent(例如 '%SOMETHING%')
useragent 大小 1024 varchar
数据可以是旧的,也可以是新的。我无法使用日期过滤器
这是我经常使用的批量删除。也许它会给您一些关于如何满足您的需求的想法。我创建一个存储过程并从 SQL 代理作业调用该过程。我通常安排它允许在执行之间进行事务日志备份,这样日志就不会变得太大。如果您愿意,您可以随时以交互方式运行它。
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE PROC [DBA_Delete_YourTableName] AS
SET NOCOUNT ON;
---------------------------------------------------------
DECLARE @DaysHistoryToKeep INT
SET @DaysHistoryToKeep = 90
IF @DaysHistoryToKeep < 30
SET @DaysHistoryToKeep = 30
---------------------------------------------------------
DECLARE @continue INT
DECLARE @rowcount INT
DECLARE @loopCount INT
DECLARE @MaxLoops INT
DECLARE @TotalRows BIGINT
DECLARE @PurgeThruDate DATETIME
SET @PurgeThruDate = DATEADD(dd,(-1)*(@DaysHistoryToKeep+1), GETDATE())
SET @MaxLoops = 100
SET @continue = 1
SET @loopCount = 0
SELECT @TotalRows = (SELECT COUNT(*) FROM YourTableName (NOLOCK) WHERE CREATEDDATETIME < @PurgeThruDate)
PRINT 'Total Rows = ' + CAST(@TotalRows AS VARCHAR(20))
PRINT ''
WHILE @continue = 1
BEGIN
SET @loopCount = @loopCount + 1
PRINT 'Loop # ' + CAST(@loopCount AS VARCHAR(10))
PRINT CONVERT(VARCHAR(20), GETDATE(), 120)
BEGIN TRANSACTION
DELETE TOP (4500) YourTableName WHERE CREATEDDATETIME < @PurgeThruDate
SET @rowcount = @@rowcount
COMMIT
PRINT 'Rows Deleted: ' + CAST(@rowcount AS VARCHAR(10))
PRINT CONVERT(VARCHAR(20), GETDATE(), 120)
PRINT ''
IF @rowcount = 0 OR @loopCount >= @MaxLoops
BEGIN
SET @continue = 0
END
END
SELECT @TotalRows = (SELECT COUNT(*) FROM YourTableName (NOLOCK) WHERE CREATEDDATETIME < @PurgeThruDate)
PRINT 'Total Rows Remaining = ' + CAST(@TotalRows AS VARCHAR(20))
PRINT ''
GO
过滤条件是... ip in(大约20个ip列表)和useragent,如'%SOMETHING%'
关于表大小,执行删除时触及尽可能少的行非常重要。
我想象在一个大小的表上,您已经在
ip
列上有一个索引。将列表中的 20 个左右的 ip 放在表中而不是放在 in
子句中可能会有所帮助(或没有帮助),特别是如果它们是参数的话。我会查看我的查询计划来看看。 我希望
useragent like '%SOMETHING%'
通常是真的;否则,这是一个昂贵的测试,因为 SQL Server 必须检查每一行是否符合条件 ip
。如果不是,重新设计以允许查询避免 like
可能会有所帮助。 [D]删除较小的百分比并不真正相关。使用选择性搜索标准(根据上述),以及以“绝对”术语表示的删除事务的大小。根据定义,删除的行数和行大小决定了事务的大小。非常大的事务可能会占用机器资源。在这种情况下,将它们分成更小的部分可以产生更好的性能。 我使用的最后一台服务器有 0.25 TB RAM,可以轻松地一次删除 100 万行,但不能删除 1000 万行。您的里程
会有所不同;你必须尝试、观察、观察。 您愿意对机器征税多少将取决于同时运行(或需要能够)运行的其他内容。将一个逻辑操作(删除 [条件] 处的所有行)分解为“块”的方式还取决于您希望数据库在删除过程正在进行时、某些块被删除而其他块保留时的样子展示。
如果您确实决定将其分成块,我建议
不使用固定行数和TOP(n)
语法,因为这是“最不合逻辑”的解决方案。除非您使用
order by
,否则您将让服务器任意选择要删除的 N 行。如果您确实使用 order by
,则需要服务器在开始删除之前对结果进行排序,可能在整个运行过程中进行多次。哎呀!
相反,找到行的一些逻辑子集,理想情况下可以沿着聚集索引区分,这些子集低于机器可接受的一次删除行数的阈值。循环该集合。在您的情况下,我很想迭代 ip
子句中的 in
值集。你得到的不是
delete ... where ip in(...)
,而是(大致)for each ip delete ... where ip = @ip
后一种方法的优点是您始终知道数据库的位置。如果您终止该过程或者它在迭代过程中回滚,您可以检查数据库以查看仍保留哪些 ip(或您最终使用的任何标准)。您可以避免任何类型的病态行为,即某些查询得到部分结果,因为您的选择标准的某些部分(仅由服务器确定)存在而其他部分被删除。在思考这个问题时,您可以说,
我无法删除 ip 192.168.0.1,因为,而不用想知道哪一部分已被删除。
总而言之,我推荐:
让服务器有机会只触及您想要影响的行,并验证它将执行的操作。构建删除例程(如果需要)来删除逻辑块,以便您可以随时推断数据库的状态。