3

列にnull値を入力する次の手順があります。データセットが非常に少ない場合、この手順は正常に機能します。しかし、私がターゲットにしているデータは約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;
4

1 に答える 1

12

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は生成されません。

于 2011-11-01T20:13:29.210 に答える