0

我有一张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;
/
4

3 回答 3

4

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

于 2013-10-11T18:31:19.820 回答
4

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

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

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

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

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

于 2013-10-11T18:32:06.623 回答
1

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

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

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

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

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

于 2013-10-11T18:43:13.923 回答