2011-11-01 186 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我已經更新了我的答案。 –