【问题标题】:PL/SQL script running very slowPL/SQL 脚本运行很慢
【发布时间】:2013-10-19 22:18:37
【问题描述】:

我有一张表mytable(**id**,colA,colB),有 500 万条记录。 colA, colB 没有其他约束。我必须将 colB 中的值复制到 colA 并使 colB 为空。这是我在下面创建的过程。有时它在 5 分钟内运行,有时需要 45 分钟。这个脚本有什么问题?我确定在此期间没有其他进程访问此表。我该如何优化呢? (我知道还有很多其他因素会影响这个速度,比如数据库引擎本身很慢,可能是当时机器满负荷运行。我正在寻找我手中的东西,即我的脚本。 )

 DECLARE


      l_update_total pls_integer := 0;

      CURSOR cur IS SELECT id, colB FROM mytable where colA is null;
      TYPE t_recs IS TABLE OF cur%ROWTYPE;
      l_loop_count pls_integer := 0;
      l_recs t_recs;
      l_rec cur%ROWTYPE;


    BEGIN

       OPEN cur;
       LOOP

            FETCH cur BULK COLLECT INTO l_recs LIMIT 500;

            EXIT WHEN l_recs.COUNT = 0;

            FOR indx IN 1 .. l_recs.COUNT 
            LOOP

                l_rec := l_recs(indx);
                UPDATE mytable SET colB=null,colA = l_rec.colB WHERE id = l_rec.id;
                l_update_total := l_update_total + SQL%ROWCOUNT;


            END LOOP;

            COMMIT;

       END LOOP;

END;
/

【问题讨论】:

  • 脚本有什么问题是它使用了游标……在这种格式的 500 万条记录中,您正在运行 500 万次更新。 SQL 是一种基于集合的语言,而不是线性语言,因此 500 万行的更新比单行的 500 万更新更快(可能快 500000%)......你会想要重写它,所以它是一个语句而不是 2循环

标签: sql performance oracle plsql


【解决方案1】:

你为什么不写一个简单的 SQL 语句

UPDATE mytable
   SET colB = null,
       colA = colB
 WHERE colA IS NULL;

纯 SQL 解决方案将比 PL/SQL 解决方案更快。

但是,如果您不尝试理解为什么您的运行时会发生变化,那么要提高性能就会异常困难。如果系统超载到您的过程运行时间增加了 40 分钟,而这一切都不是由于锁定(因为您声称没有其他会话正在读取或写入有问题的表),那么完全有可能它将增加单个 SQL 语句的运行时间 40 分钟。

如果您坚持使用较慢的 PL/SQL 方法,至少在循环中取出 commit 以避免每批都产生这种开销。

【讨论】:

  • 抱歉延迟回复。但我真的可以这样做吗? colA 也变为 null 不是吗?我会马上在一个小片场上试一试。
  • @Naresh - 我发布的UPDATE 声明会将colA 设置为colB 的旧值/现有值,并将colB 设置为NULL。您不会将colA 设置为NULL(当然,除非现有的colB 值已经是NULL)。
  • 对此感兴趣的朋友,SQLFiddle here
【解决方案2】:

转储游标和过程逻辑,并将其重写为 SQL。

【讨论】:

    【解决方案3】:

    数据库查询并不意味着逐行执行(RBAR,对于 Jeff Moden 粉丝)。考虑数据集,而不是行。

    从技术上讲,一个简单的更新会更好:

    UPDATE mytable SET colB=null, colA=colB WHERE colA is null
    

    由于您似乎确定在操作期间没有其他进程接触此表,因此锁定应该不是问题。然而,可用资源很可能是(考虑到记录的数量)。

    如果在单个查询中执行操作对服务器来说过于苛刻,请尝试使用混合解决方案,一次更新几千行,每次使用单个 UPDATE 语句。我的 PL/SQL 日子还很长,从那时起已经有很多 T-SQL,所以我现在不能给你确切的合成器,但想法还是一样的。

    【讨论】:

    • 如何一次更新 5000 行?为了做到这一点,我必须拿到 5000 把钥匙?所以我需要另一个查询,对吗?你能给我举个例子吗?
    • 在 Oracle 中,执行单个事务几乎总是比在“块”中执行更新更快(与 SQL Server 相比)。只有当其他会话想要修改这些值时,锁定才是一个问题。在 Oracle 中,读取器从不阻塞写入器,写入器从不阻塞读取器。
    猜你喜欢
    • 2013-07-12
    • 2018-01-22
    • 1970-01-01
    • 2015-11-18
    • 1970-01-01
    • 2014-01-21
    • 1970-01-01
    • 1970-01-01
    • 2021-12-29
    相关资源
    最近更新 更多