如何高效地从大型sql表中删除少量数据

问题描述 投票:0回答:3

我想从 1TB 表中删除 10GB (1%) 数据。我读过几篇关于从大表中删除大量数据的文章,但没有找到太多关于从大表中删除较小比例数据的文章。

其他详细信息: 尝试从访问表中删除机器人数据。过滤条件是字段的组合... ip in(大约 20 个 ip 列表)和 useragent(例如 '%SOMETHING%')

useragent 大小 1024 varchar

数据可以是旧的,也可以是新的。我无法使用日期过滤器

sql sql-server delete-row
3个回答
1
投票

这是我经常使用的批量删除。也许它会给您一些关于如何满足您的需求的想法。我创建一个存储过程并从 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

1
投票

过滤条件是... ip in(大约20个ip列表)和useragent,如'%SOMETHING%'

关于表大小,执行删除时触及尽可能少的行非常重要。

  1. 我想象在一个大小的表上,您已经在

    ip
    列上有一个索引。将列表中的 20 个左右的 ip 放在表中而不是放在
    in
    子句中可能会有所帮助(或没有帮助),特别是如果它们是参数的话。我会查看我的查询计划来看看。

  2. 我希望

    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,因为
,而不用想知道哪一部分已被删除。

总而言之,我推荐:

让服务器有机会只触及您想要影响的行,并验证它将执行的操作。

构建删除例程(如果需要)来删除逻辑块,以便您可以随时推断数据库的状态。
  • naw 我有类似的问题,我的解决方案是,在我删除的表的依赖项中,有一些其他大表,其中我表中的 pk 作为外键。没有它的索引。我把它们放进去之后就好多了。 对不起我的英语不好。 -- 查看估计的执行计划。

0
投票

© www.soinside.com 2019 - 2024. All rights reserved.