一尘不染

以10,000个批处理的方式更新oracle DB中的1百万条记录;

sql

我必须将oracle db中的表更新为10k批处理。

我尝试了这个:

BEGIN
        WHILE (true) LOOP
        UPDATE TOP (10000) CUSTOMERS SET ACTIVE = 'N' WHERE ACTIVE='Y';
        IF sql%notfound THEN
                EXIT;
        END IF;
        COMMIT;
        END LOOP;
END;

它不起作用,因为plsql不支持top。

有什么建议?


阅读 129

收藏
2021-05-16

共1个答案

一尘不染

使用JVA的建议,您的pl / SQL块应按以下方式完成(因为您是pl / sql的新手,所以我添加了一些您可能感兴趣的语法建议):

BEGIN
      -- WHILE (TRUE) can be omitted: "loop ... end loop;" 
      --  already is an endless loop
      LOOP
         UPDATE CUSTOMERS 
             SET ACTIVE = 'N' 
         WHERE ACTIVE='Y'
           AND rownum <= 1000;  
         exit when sql%notfound; -- notice that exit accepts "when condition"
         --IF sql%notfound THEN  -- you can avoid a if/endif by using "exit when"
         --   EXIT;
         -- END IF;
         COMMIT;
   END LOOP;
   commit; -- you missed this commit for the last iteration
END;

不要试图将“ commit”放在“ sql%notfound退出时”之前:在“ commit”
sql%notfound之后总是错误的,您的循环将是无休止的。

让我指出,为了提高效率,此方法需要对“ ACTIVE”列进行索引!

如果您在“活动”列上没有索引,则每次“更新”都将被迫从头开始重新启动全表扫描,以查找仍需要更新的下1000条记录。

我提议的另一种方法使用了一些高级PL / SQL功能,作为学习者,您对mighy感兴趣(rowid,“ table of”,光标批量获取和“
forall”),并且只对要更新的表进行了一次扫描因此(在没有索引的情况下)它的性能比以前的方法要好。请记住,如果您有索引,这会比较慢
(但是使用foralls,批量收集和rowid访问不会那么慢) ,但是在事情更复杂的情况下(例如:当where条件时
会派上用场需要使用无法提高速度的复杂联接访问其他表中的数据)。在某些情况下,“ where”是如此复杂且缓慢,以至于您真的不想使用“ where
rownum <= 1000”方法一遍又一遍地重新执行它。

    declare
      type rowid_array is table of rowid;

      ids rowid_array;

      cursor cur is 
         select rowid as id 
         from CUSTOMERS 
         where ACTIVE='Y';

    begin
      open cur;
      loop
         fetch cur bulk collect into ids limit 1000;
         exit when ids.count = 0;

         forall c in ids.first .. ids.last 
            update CUSTOMERS set  ACTIVE='N';

         commit;     
      end loop;
    end;
2021-05-16