我有以下程序填充列中的空值。如果我有非常小的数据集,该过程工作正常。但我所针对的数据大约有30亿条记录。只需在100万条记录中测试这个脚本就会抛出这些行为。
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 :(得分:11)
首先,您通常不会使用DBMS_OUTPUT
进行日志记录。将数据写入日志表通常更有意义,特别是如果您的日志记录过程被定义为自治事务,以便您可以在过程运行时监视日志数据。 DBMS_OUTPUT
仅在整个程序执行完毕后才显示,此时通常有点无意义。
与第一点相关,依靠DBMS_OUTPUT
向呼叫者表明存在某种异常是一种非常糟糕的做法。至少,您需要重新引发抛出的异常,以便获得错误堆栈以调试问题。
其次,当您启用输出时,您必须指定DBMS_OUTPUT
可以写入的缓冲区的大小。您似乎已将缓冲区声明为20,000字节,如果只是
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。