SQL对数百万行重复删除查询以提高性能

时间:2020-03-06 15:01:37  来源:igfitidea点击:

这是一次冒险。我从上一个问题中的循环重复查询开始,但是每个循环将遍历所有1700万条记录,这意味着将需要数周的时间(使用MSSQL仅运行* select count * from MyTable *就需要4:30分钟2005年)。我从这个网站和这篇文章中闪现了信息。

并已经到达下面的查询。问题是,对于任何类型的性能而言,这是否是对1700万条记录运行的正确查询类型?如果不是,那是什么?

SQL查询:

DELETE tl_acxiomimport.dbo.tblacxiomlistings
WHERE RecordID in 
(SELECT RecordID
    FROM tl_acxiomimport.dbo.tblacxiomlistings
    EXCEPT
    SELECT RecordID
    FROM (
        SELECT RecordID, Rank() over (Partition BY BusinessName, latitude, longitude,           Phone ORDER BY webaddress DESC, caption1 DESC, caption2 DESC ) AS Rank
    FROM tl_acxiomimport.dbo.tblacxiomlistings
    ) al WHERE Rank = 1)

解决方案

在查询分析器中运行此命令:

SET SHOWPLAN_TEXT ON

然后要求查询分析器运行查询。 SQL Server会生成查询计划并将其放入结果集中,而不是运行查询。

向我们显示查询计划。

看起来不错,但我们可以考虑将数据选择到临时表中,然后在delete语句中使用它。我注意到通过执行此操作而不是在一个查询中全部执行,可以显着提高性能。

1700万条记录算不了什么。如果只需要4:30进行一次选择计数(*),则存在一个严重的问题,可能与服务器内存不足或者处理器真的很旧有关。

为了提高性能,请修理机器。将其抽至2GB。如今,RAM是如此便宜,以至于其成本远远低于时间。

查询进行时,处理器或者磁盘是否在抖动?如果没有,则说明有什么阻止呼叫。在这种情况下,我们可以考虑将数据库置于单用户模式下运行清理所需的时间。

因此,我们要删除所有未排在第一位的记录吗?可能需要将连接与前1个子查询的连接进行比较(这也可能在2000年有效,因为排名仅在2005年及以上)。

我们是否需要在一次操作中删除所有重复项?我假设我们正在执行某种内部管理任务,我们可能可以分段执行。

基本上创建一个游标,该游标循环所有记录(脏读)并删除每个记录的重复项。总体而言,它会慢很多,但是每个操作都相对最少。这样,家务就成了一项固定的后台任务,而不是每夜进行一次。

数据库,服务器,存储或者它们的某种组合有问题。选择计数的4:30 *似乎非常高。

运行DBCC_SHOWCONTIG来查看表的碎片程度,这可能会导致对该大小的表造成重大性能损失。

另外,要添加RyanKeeter的注释,请运行显示计划,如果有任何表扫描,请为该表上的PK字段创建索引。

上面建议我们首先进入临时表的建议是最好的选择。我们还可以使用类似:

set rowcount 1000

在运行删除之前。删除1000行后,它将停止运行。然后一次又一次地运行它,直到删除0条记录。

看到QueryPlan会有所帮助。

这可行吗?

SELECT m.*
into #temp
FROM tl_acxiomimport.dbo.tblacxiomlistings m 
inner join (SELECT RecordID, 
                   Rank() over (Partition BY BusinessName, 
                                             latitude,  
                                             longitude,            
                                             Phone  
                                ORDER BY webaddress DESC,  
                                         caption1 DESC,  
                                         caption2 DESC ) AS Rank
              FROM tl_acxiomimport.dbo.tblacxiomlistings
           ) al on (al.RecordID = m.RecordID and al.Rank = 1)

truncate table tl_acxiomimport.dbo.tblacxiomlistings

insert into tl_acxiomimport.dbo.tblacxiomlistings
     select * from #temp

如果我正确理解,查询与

DELETE tl_acxiomimport.dbo.tblacxiomlistings
FROM
    tl_acxiomimport.dbo.tblacxiomlistings allRecords
    LEFT JOIN (   
        SELECT RecordID, Rank() over (Partition BY BusinessName, latitude, longitude, Phone ORDER BY webaddress DESC, caption1 DESC, caption2 DESC ) AS Rank
        FROM tl_acxiomimport.dbo.tblacxiomlistings
        WHERE Rank = 1) myExceptions
    ON allRecords.RecordID = myExceptions.RecordID
WHERE
    myExceptions.RecordID IS NULL

我认为应该运行得更快,在可能的情况下,我倾向于避免使用" IN"子句来支持JOIN。

我们实际上可以通过简单地在FROM部分上调用SELECT *或者SELECT COUNT(*)来安全地测试速度和结果,例如

SELECT *
FROM
    tl_acxiomimport.dbo.tblacxiomlistings allRecords
    LEFT JOIN (   
        SELECT RecordID, Rank() over (Partition BY BusinessName, latitude, longitude, Phone ORDER BY webaddress DESC, caption1 DESC, caption2 DESC ) AS Rank
        FROM tl_acxiomimport.dbo.tblacxiomlistings
        WHERE Rank = 1) myExceptions
    ON allRecords.RecordID = myExceptions.RecordID
WHERE
    myExceptions.RecordID IS NULL

这就是为什么我更喜欢JOIN方法的另一个原因
希望对我们有所帮助

这样做会不会更简单:

DELETE tl_acxiomimport.dbo.tblacxiomlistings
WHERE RecordID in 
(SELECT RecordID
   FROM (
        SELECT RecordID,
            Rank() over (Partition BY BusinessName,
                                  latitude,
                                  longitude,
                                  Phone
                         ORDER BY webaddress DESC,
                                  caption1 DESC,
                                  caption2 DESC) AS Rank
        FROM tl_acxiomimport.dbo.tblacxiomlistings
        )
  WHERE Rank > 1
  )

除了按照建议使用truncate之外,我还很幸运使用此模板从表中删除了很多行。我不记得使用它,但是我认为使用事务有助于防止日志文件增长-尽管这可能是另一个原因-不确定。而且我通常在执行以下操作之前将事务日志记录方法切换为简单方法:

SET ROWCOUNT 5000
WHILE 1 = 1
BEGIN
    begin tran
            DELETE FROM ??? WHERE ???
            IF @@rowcount = 0
            BEGIN
               COMMIT
               BREAK
            END
    COMMIT
END
SET ROWCOUNT 0

请记住,进行大型删除时最好先做好备份(而且我通常还会将已删除的记录复制到另一个表中,以防万一,我需要立即恢复它们。)