【问题标题】:SQL Duplicate Delete Query over Millions of Rows for Performance数百万行的 SQL 重复删除查询以提高性能
【发布时间】:2010-09-14 19:24:25
【问题描述】:

这是一次冒险。我从位于 my previous question 的循环重复查询开始,但每个循环都会遍历所有 1700 万条记录这意味着需要数周时间(仅运行 *select count * from MyTable* 需要我的服务器 4:30 分钟使用 MSSQL 2005)。我从这个网站和post 获得了一些信息。

并且已经到达下面的查询。问题是,对于任何类型的性能,这是对 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)

【问题讨论】:

    标签: sql sql-server duplicate-data sql-delete


    【解决方案1】:

    查看 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
    

    【讨论】:

    • TrickyNixon,我将如何在输出中显示查询计划以便您阅读?我可以显示预计的执行情况,您能帮忙吗?
    • 这个确实有效,而且效果非常好,非常感谢。
    • 确保您保留了所有应有的记录!
    【解决方案2】:

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

    运行 DBCC_SHOWCONTIG 以查看您的表有多碎片化,这可能会导致该大小的表的性能受到重大影响。

    此外,要添加到 RyanKeeter 的评论,运行显示计划,如果有任何表扫描,则为该表上的 PK 字段创建索引。

    【讨论】:

    • 在 PK 字段上创建一个(其他)索引很少有用。
    【解决方案3】:

    这样做不是更简单吗:

    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
      )
    

    【讨论】:

    • 是的,但是我们将无法使用我刚刚学到的非常酷的 EXCEPT 子句....spoilsport :-)
    【解决方案4】:

    在查询分析器中运行:

    SET SHOWPLAN_TEXT ON
    

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

    向我们展示查询计划。

    【讨论】:

    • 您是如何做到这一点的?我可以显示预计的计划,但在 SQL Server 2005 中,我将如何显示您要查找的内容?
    • 第 1 步:运行我在查询窗口 "SET SHOWPLAN_TEXT ON" 中描述的命令。第 2 步:在同一窗口中运行您要分析的查询。
    【解决方案5】:

    1700 万条记录不算什么。如果只执行一次 select count(*) 需要 4:30,则存在严重问题,可能与服务器内存不足或处理器非常旧有关。

    为了性能,修理机器。将其泵送至 2GB。现在 RAM 是如此便宜,以至于它的成本远远低于您的时间。

    执行该查询时处理器或磁盘是否抖动?如果不是,那么某些东西正在阻止呼叫。在这种情况下,您可以考虑将数据库置于单用户模式下运行清理所需的时间。

    【讨论】:

      【解决方案6】:

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

      您是否需要在一次操作中删除所有重复项?我假设您正在执行某种家政任务,您也许可以分段完成。

      基本上创建一个循环所有记录(脏读)并删除每个记录的游标。总体上会慢很多,但每个操作都相对较少。然后你的家务就变成了一个持续的后台任务,而不是每晚的批处理。

      【讨论】:

        【解决方案7】:

        上面的建议是首先选择临时表是您的最佳选择。你也可以使用类似的东西:

        set rowcount 1000
        

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

        【讨论】:

        • 过去我们在删除大量数据时使用 RowCount 技巧。它可以防止交易变得太大。
        【解决方案8】:

        如果我没听错,你的查询是一样的

        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 方法的另一个原因 希望对你有帮助

        【讨论】:

          【解决方案9】:

          这看起来不错,但您可能会考虑将您的数据选择到一个临时表中并在您的删除语句中使用它。我注意到这样做而不是在一个查询中完成所有操作可以带来巨大的性能提升。

          【讨论】:

            【解决方案10】:

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

            【讨论】:

              【解决方案11】:

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

              设置行数 5000 而 1 = 1 开始 开始翻译 删除???在哪里 ??? 如果@@rowcount = 0 开始 犯罪 休息 结尾 犯罪 结尾 设置行数 0

              【讨论】:

                猜你喜欢
                • 2017-10-01
                • 1970-01-01
                • 2019-10-27
                • 2018-12-26
                • 2023-01-05
                • 1970-01-01
                • 1970-01-01
                • 2013-10-08
                • 1970-01-01
                相关资源
                最近更新 更多