sql - SQL UPDATE 语句所需的优化建议。使用了大约 500 万个记录表

标签 sql optimization query-optimization sas

我正在寻找任何建议来优化 SAS 程序中的以下 PROC SQL 语句。涉及的两个表每个包含大约 500 万条记录,运行时间约为 46 小时。

该语句希望更新“旧”表的"new"版本。如果“旧”表的“PK_ID”列没有“3RD_ID”和“CODE”的值,则注意一列,但在"new"表中,对于“PK_ID”,它现在列出“3RD_ID”和“CODE”的值。

感谢您的任何建议......(代码的格式确实如下!由于某些原因,我的空格没有显示缩进......)

PROC SQL _METHOD;  
 UPDATE NEW_TABLE AS N  
   SET NEW_2ND_ID=(SELECT 2ND_ID FROM OLD_TABLE AS O  
                WHERE N.PK_ID=0.PK_ID  
                  AND N.2ND_ID<>O.2ND_ID  
                  AND O.3RD_ID IS NULL  
                  AND O.CODE IS NULL  
                  AND N.3RD_ID IS NOT NULL  
                  AND N.CODE IS NOT NULL  
                  AND N.2ND_ID IS NOT NULL)  
        WHERE N.3RD_ID IS NOT NULL  
          AND N.PK_ID IS NOT NULL  
          AND N.CODE IS NOT NULL  
          AND N.2ND_ID IS NOT NULL;  
QUIT;   

最佳答案

我不熟悉您使用的 SQL 变体。但是,无论您是否获得更好的性能,都应该使用 ANSI 连接语法。这是它在 T-SQL 中的样子,请为您的系统修改它:

UPDATE N
SET N.2ND_ID = O.2ND_ID
FROM
   NEW_TABLE AS N
   INNER JOIN OLD_TABLE AS O ON N.PK_ID = O.PK_ID
WHERE
   N.2ND_ID <> O.2ND_ID
   AND N.3RD_ID IS NOT NULL  
   AND O.3RD_ID IS NULL  
   AND N.CODE IS NOT NULL  
   AND O.CODE IS NULL

请注意,我删除的额外条件不是必需的,例如 N.2ND_ID <> O.2ND_ID已经保证这两列不为空。

但是,在两个 500 万行的表上,您将获得糟糕的性能。这里有一些想法可以加快速度。我敢打赌,通过正确的策略组合,您可以将时间缩短到一个小时以内。
  • 将更新拆分为批次(小块,循环遍历整个集合)。虽然这听起来与“不要循环,使用集合”的正常数据库建议背道而驰,但实际上并非如此:您只是使用较小的集合,而不是在行级别循环。像这样批量更新的最好方法是“遍历聚集索引”。我不确定该术语在您使用的 DBMS 中是否有意义,但本质上它意味着根据它们在您正在更新的表对象中找到的顺序来选择您在每个循环期间更新的块。 PK_ID 听起来像是使用的候选,但如果原始表数据不是按此列排序,那么它会变得更加复杂。在 T-SQL 中,批处理循环可能如下所示:
    DECLARE
       @ID int,
       @Count int
    
    SET @ID = 1
    SET @Count = 1
    
    WHILE @Count > 0 BEGIN
       UPDATE N
       SET N.2ND_ID = O.2ND_ID
       FROM
          NEW_TABLE AS N
          INNER JOIN OLD_TABLE AS O ON N.PK_ID = O.PK_ID
       WHERE
          N.2ND_ID <> O.2ND_ID
          AND N.3RD_ID IS NOT NULL  
          AND O.3RD_ID IS NULL  
          AND N.CODE IS NOT NULL  
          AND O.CODE IS NULL
          AND N.PK_ID BETWEEN @ID AND @ID + 4999
       SET @Count = @@RowCount
       SET @ID = @ID + 5000
    END
    

    此示例假设您的 PK_ID 列密集,每次更新将真正达到 5000 行。如果不是这种情况,则切换到使用 TOP 5000 的方法,并将更新的 PK_ID 输出到表中,或者一步找到下一次更新的 @StartID 和 @EndID,然后执行它。

    根据我的经验,好的批量大小往往在 1000 到 20000 行之间。在 MS-SQL 服务器中,甜蜜点似乎刚好低于强制从搜索切换到扫描的数字(因为最终,数据库引擎假设单次扫描比多次搜索便宜,尽管它通常是处理 500 万行表时出错)。
  • 首先选择要更新到工作/临时表中的 ID 和数据,然后加入该表。这个想法是用一个简单的 INSERT 语句来解决大扫描的问题,然后将索引添加到临时表并执行更新,而无需复杂的 WHERE 子句。一旦表只包含要更新的行和所需的列,不仅 WHERE 子句会丢失大部分条件,而且临时表每页的行数更少,行数更多(因为它没有无关的列),这将大大提高性能。这甚至可以分阶段完成,其中创建新表的“影子”,然后是旧表的“影子”,然后是它们之间的连接,最后连接回新表以更新它。虽然这听起来像很多工作,但我想你会惊讶于它可以提供的完全疯狂的完成速度。
  • 您可以采取任何措施将旧表中的读取转换为搜索而不是扫描,这将有所帮助。您可以采取任何措施来减少用于保存临时数据的磁盘数量(例如 500 万行的巨大哈希表)。
  • 关于sql - SQL UPDATE 语句所需的优化建议。使用了大约 500 万个记录表,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2155177/

    相关文章:

    sql - 寻找用于 SQL 脚本执行的通用库(又名 RDBMS 基准测试库)

    sql - Transact-SQL 总结耗时

    sql - 根据不同的条件在同一个表上多次连接与在聚合表上连接一次

    c# - 同时解码和显示多个 H264 视频的最快方法 C#

    sql - 在时间戳列上为使用年份函数的查询创建索引

    java - 错误: No operations allowed after statement closed

    mysql 5.2.3 在触发器内调用的过程出现语法错误

    java - 从映射表中获取一个查询中的数据

    MySQL查询优化-加速|索引使用

    c# - MSSQL 快速更新