web-dev-qa-db-ja.com

OracleのPL / SQLから出力をフラッシュする方法はありますか?

シェルスクリプト内から呼び出され、実行に時間がかかるSQLスクリプトがあります。現在、dbms_output.put_lineさまざまな時点でのステートメント。これらのprintステートメントからの出力はログファイルに表示されますが、スクリプトが完了すると1回だけです。

スクリプトの実行中に出力がログファイルに表示されるようにする方法はありますか?

42
baxter

あんまり。 DBMS_OUTPUTの機能は次のとおりです。PL/ SQLブロックは、クライアントとの対話なしでデータベースサーバー上で実行されます。したがって、PUT_LINEを呼び出すと、そのテキストがサーバー上のメモリ内のバッファーに入れられます。 PL/SQLブロックが完了すると、制御がクライアントに返されます(この場合はSQLPlusを想定しています)。その時点で、クライアントはGET_LINEを呼び出してバッファからテキストを取得し、表示します。

したがって、ログファイルに出力をより頻繁に表示させる唯一の方法は、大きなPL/SQLブロックを複数の小さなブロックに分割することです。そのため、制御がより頻繁にクライアントに返されます。これは、コードの実行内容によっては実用的ではない場合があります。

他の方法としては、UTL_FILEを使用してテキストファイルに書き込みます。このファイルはいつでもフラッシュできます。または、自律トランザクションプロシージャを使用して、データベーステーブルにデバッグステートメントを挿入し、各ステートメントの後にコミットします。

51
Dave Costa

可能であれば、dbms_output.put_lineの呼び出しを独自の関数に置き換える必要があります。

この関数のコードは次のとおりですWRITE_LOG-2つのロギングソリューションから選択できるようにする場合:

自律トランザクションでテーブルにログを書き込む

CREATE OR REPLACE PROCEDURE to_dbg_table(p_log varchar2)
  -- table mode: 
  -- requires
  -- CREATE TABLE dbg (u varchar2(200)   --- username
  --                 , d timestamp       --- date
  --                 , l varchar2(4000)  --- log 
  -- );
AS
   pragma autonomous_transaction;
BEGIN
  insert into dbg(u, d, l) values (user, sysdate, p_log);
  commit;
END to_dbg_table;
/

または、データベースをホストするDBサーバーに直接書き込みます

これは、OracleディレクトリTMP_DIR

CREATE OR REPLACE PROCEDURE to_dbg_file(p_fname varchar2, p_log varchar2)
  -- file mode: 
  -- requires
--- CREATE OR REPLACE DIRECTORY TMP_DIR as '/directory/where/Oracle/can/write/on/DB_server/';
AS
  l_file utl_file.file_type;
BEGIN
  l_file := utl_file.fopen('TMP_DIR', p_fname, 'A');
  utl_file.put_line(l_file, p_log);
  utl_file.fflush(l_file);
  utl_file.fclose(l_file);
END to_dbg_file;
/


WRITE_LOG

次に、WRITE_LOG2つの使用を切り替えることができるプロシージャ、またはパフォーマンスの損失を避けるために非アクティブにするプロシージャ(g_DEBUG:=FALSE)。

CREATE OR REPLACE PROCEDURE write_log(p_log varchar2) AS
  -- g_DEBUG can be set as a package variable defaulted to FALSE
  -- then change it when debugging is required
  g_DEBUG boolean := true;
  -- the log file name can be set with several methods...
  g_logfname varchar2(32767) := 'my_output.log';
  -- choose between 2 logging solutions:
  -- file mode: 
  g_TYPE varchar2(7):= 'file';
  -- table mode: 
  --g_TYPE varchar2(7):= 'table';
  -----------------------------------------------------------------
BEGIN
  if g_DEBUG then
    if g_TYPE='file' then
      to_dbg_file(g_logfname, p_log);
    elsif g_TYPE='table' then
      to_dbg_table(p_log);
    end if;
  end if;  
END write_log;
/

そして、上記のテスト方法は次のとおりです。

1)SQLPLUSからこの(file mode)を起動します。

BEGIN
  write_log('this is a test');
  for i in 1..100 loop
    DBMS_LOCK.sleep(1);
    write_log('iter=' || i);
  end loop;
  write_log('test complete');
END;
/

2)データベースサーバーで、シェルを開き、

 tail -f -n500 /directory/where/Oracle/can/write/on/DB_server/my_output.log
6
J. Chomel

2つの選択肢:

  1. 自律型トランザクションを使用して、ロギングテーブルにロギングの詳細を挿入できます。このロギングテーブルは、別のSQLPLUS/Toad/sql開発者などのセッションでクエリできます。メインのSQLスクリプトでトランザクション処理を妨げることなくロギングをコミットできるようにするには、自律型トランザクションを使用する必要があります。

  2. 別の方法は、ログ情報を返すパイプライン関数を使用することです。例についてはこちらをご覧ください: http://berxblog.blogspot.com/2009/01/pipelined-function-vs-dbmsoutput.html 使用する場合別のSQLPLUS/Toad/sql開発者などのセッションを使用する必要がないパイプライン関数。

6
tuinstoel

DBMS_OUTPUTのバッファは、プロシージャDBMS_OUTPUT.get_lineが呼び出されたときに読み込まれます。クライアントアプリケーションがSQL * Plusである場合、プロシージャが終了した後にのみフラッシュされることを意味します。

このSO で説明されている方法を適用して、DBMS_OUTPUTバッファーをファイルに書き込むことができます。

3
Vincent Malgrat

PL/SQL環境からシステムシェルにアクセスできる場合は、netcatを呼び出すことができます。

 BEGIN RUN_Shell('echo "'||p_msg||'" | nc '||p_Host||' '||p_port||' -w 5'); END;

p_msg-ログメッセージですv_Hostは、ポートv_portのソケットからデータを読み取るpythonスクリプトを実行しているホストです。

aplogr をシェルとpl/sqlのリアルタイムログモニタリング用に記述したときに、この設計を使用しました。

1
olekb