2011-11-01 184 views
3

我有以下过程填写列中的空值。如果我有非常小的一组数据,该过程正常工作。但是我的目标数据是大约30亿的记录。只有在百万条记录上测试这个脚本才会抛出这些错误。过程缓冲区溢出

ORA-20000: ORU-10027: buffer overflow, limit of 20000 bytes 
ORA-06512: at "SYS.DBMS_OUTPUT", line 32 
ORA-06512: at "SYS.DBMS_OUTPUT", line 97 
ORA-06512: at "SYS.DBMS_OUTPUT", line 112 
ORA-06512: at "DBNAME.PRBACKFILLI", line 39 
ORA-06512: at line 2 

具有一点点挖掘后,我意识到DBMS_OUTPUT.PUT_LINE打印输出在该过程的末尾。现在的事情是我们想要调试信息,我们该怎么做?

CREATE OR REPLACE PROCEDURE PRBACKFILL (str_dest IN VARCHAR2) AS 
    CURSOR cr_pst_ IS 
    select id, seq from TABLE_ where ID is null; 

    TYPE t_id_array IS TABLE OF NUMBER INDEX BY BINARY_INTEGER; 
    TYPE t_seq_array IS TABLE OF NUMBER INDEX BY BINARY_INTEGER; 

    a_id t_id_array; 
    a_seq t_seq_array; 
    i_bulk_limit NUMBER := 1000; 
BEGIN 
    OPEN cr_pst_; 
    LOOP 
    FETCH cr_pst_ 
    BULK COLLECT INTO a_id, a_seq LIMIT i_bulk_limit; 


    FOR i IN 1..a_id.count LOOP 
     a_id(i) := Floor(a_seq(i)/10000000000000); 
    END LOOP; 

    FORALL i IN 1 .. a_id.count 
     UPDATE TABLE_ 
     SET ID = a_id(i) 
     WHERE SEQ = a_seq(i); 

     COMMIT; 
     DBMS_OUTPUT.PUT_LINE ('COMMITED '||i_bulk_limit||' records'); 
    EXIT WHEN cr_pst_%NOTFOUND; 
    END LOOP; -- main cursor loop 
    CLOSE cr_pst_; 

    DBMS_OUTPUT.PUT_LINE ('Backfill completed gracefully!'); 

    EXCEPTION 
    WHEN NO_DATA_FOUND THEN 
     DBMS_OUTPUT.PUT_LINE('No more records to process'); 
    WHEN OTHERS THEN 
     DBMS_OUTPUT.PUT_LINE('errno: '||TO_CHAR(SQLCODE)||' Msg: ' || SQLERRM);    
END PRBACKFILL; 
. 
/
sho err; 
+0

参见http://stackoverflow.com/questions/8045844/dbms-output-size-buffer-overflow – Vadzim

回答

11

首先,您通常不会使用DBMS_OUTPUT进行记录。将数据写入日志表通常会更有意义,特别是如果将日志记录过程定义为自治事务,以便您可以在过程运行时监视日志数据。 DBMS_OUTPUT只会在整个程序执行完毕后才会显示,在这一点上它通常毫无意义。

与第一点相关,依靠DBMS_OUTPUT向调用者指出存在某种异常是非常糟糕的做法。至少,你会想重新抛出抛出的异常,以便得到错误堆栈以调试问题。

其次,当您启用输出时,您必须指定DBMS_OUTPUT可以写入的缓冲区的大小。看来,你已经声明了缓冲区20000个字节,这是默认的,如果你只是

SQL> set serveroutput on; 

您可以通过指定大小,但最大尺寸更改限制为1,000,000字节

SQL> set serveroutput on size 1000000; 

如果您计划在1000个行块中更新30亿行,那将会是一个太小的缓冲区。您将使用当前代码生成超过60倍的数据量。如果您正在使用在客户端和服务器上10.2,你应该能够分配一个无限的缓冲

SQL> set serveroutput on size unlimited; 

但不是在早期版本中的一个选项。

最后,你确定你需要首先使用PL/SQL吗?看样子,你可以更有效地做到这一点,只需执行一个UPDATE

UPDATE table_ 
    SET id = floor(seq/ 10000000000000) 
WHERE id is null; 

这是更少的代码,更容易阅读,并且会比PL/SQL替代更加高效。唯一的缺点是它要求您的UNDO表空间足够大以容纳所生成的UNDO,但将单列从NULL更新为非NULL数值不应产生太多的UNDO。

+0

我写了简化的逻辑。 UPDATE语句不起作用。 –

+1

我不确定最早的无限缓冲区是否可用,但它是10g R2中的一个选项。 SQL * Plus参考:http://download.oracle.com/docs/cd/B19306_01/server.102/b14357/ch12040.htm#sthref2862,DBMS_OUTPUT.ENABLE:http://download.oracle.com/docs/cd /B19306_01/appdev.102/b14258/d_output.htm#i999293 –

+0

@ShannonSeverance - 谢谢你的收获。它看起来像是在10.2中添加的,因为10.1文档中没有提及http://download.oracle.com/docs/cd/B14117_01/server.101/b12170/ch13。htm#sthref2817我已经更新了我的答案。 –