如何有效地从大型SQL表

时间:2017-02-08 14:10:54

标签: sql sql-server delete-row

我想从1TB表中删除10GB(1%)数据。我有几篇文章要从一个巨大的表中删除大量数据,但是从巨大的表中删除较小百分比的数据并没有太多帮助。

其他细节: 尝试从访问表中删除bot数据。过滤条件是字段组合... ip in(ip​​s列表中约有20个)和useragent类似于'%SOMETHING%'

useragent size 1024 varchar

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

2 个答案:

答案 0 :(得分:1)

这是我经常使用的块中的批量删除。也许它会给你一些关于如何满足你需求的想法。我创建一个存储过程并从SQL代理作业调用proc。我通常安排它允许在执行之间进行事务日志备份,因此日志不会变得太大。如果您愿意,您可以随时以交互方式运行它。

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 :(得分:1)

  

过滤条件是... ip in(ip​​s列表中约有20个)和useragent像'%SOMETHING%'

关于表大小,在执行删除时触摸尽可能少的行很重要。

  1. 我想象一下,在ip列上已有索引的大小的表上。它可能有助于(或不)将列表中的20个左右的ips放在表中,而不是放在in子句中,特别是如果它们是参数。我会查看我的查询计划。

  2. 我希望useragent like '%SOMETHING%'通常是真的;否则这是一个昂贵的测试,因为SQL Server必须检查符合条件的ip的每一行。如果没有,重新设计以允许查询避免like可能是有益的。

  3. [D]选择较小的百分比并不是真正相关的。使用选择性搜索条件(以上),绝对术语中删除事务的大小。根据定义,删除行的大小和行大小决定了事务的大小。非常大的事务可以推动机器资源。在这种情况下,将它们分解为较小的可以产生更好的性能。

    我使用的最后一台服务器有0.25 TB RAM,并且很容易一次删除100万行,但不是1000万行。你的变化;你必须尝试,观察,看到。

    您愿意为机器征税多少取决于同时运行(或需要能够运行)的内容。分解一个逻辑操作的方式 - 将[条件] - 删除的所有行删除到“块”中还取决于在删除过程正在进行时,当某些块被删除而其他块仍然存在时,您希望数据库看起来像什么当下。

    如果您决定将其分成块,我建议使用固定数量的行和TOP(n)语法,因为这是最不逻辑的解。除非您使用order by,否则您将离开服务器以任意选择要删除的 N 行。如果您使用order by,则需要服务器在开始删除之前对结果进行排序,可能需要在整个运行过程中多次。的Bleh!

    相反,找到一些行的逻辑子集,理想情况下可以沿聚簇索引区分,它们低于机器一次要删除的可接受行数的阈值。循环遍历该集合。在您的情况下,我很想迭代ip子句中的in值集。而不是delete ... where ip in(...),你得到(大致)for each ip delete ... where ip = @ip

    后一种方法的优点是您始终知道数据库的位置。如果您终止该过程或在其迭代的中途回滚,您可以检查数据库以查看仍然保留哪个ips(或者您最终使用的任何条件)。您可以避免任何类型的病态行为,因为某些查询会获得部分结果,因为您的选择标准的某些部分(仅由服务器确定)存在而其他部分已删除。在考虑你可以说的问题时,我无法删除ip 192.168.0.1,因为,而不知道哪个部分已被删除。

    总而言之,我建议:

    • 让服务器有机会只触摸您想要影响的行,并验证它将执行的操作。
    • 构建删除例程(如果需要)删除逻辑块,以便随时可以推断数据库的状态。