[Oracle] 動的SQLでDDL文を実行しているPL/SQLでORA-6508エラーが発生 | Archive Redo Blog

Archive Redo Blog

DBエンジニアのあれこれ備忘録

動的SQLを使用してDDL文を実行しているプロシージャやPL/SQLブロックで、
"ORA-06508 PL/SQL: コールしているプログラム単位が見つかりませんでした"

というエラーが発生することがあります。

例えば、IDを採番する順序とその順序からIDを取得してテーブルにデータを挿入するプロシージャがあり、

CREATE SEQUENCE ID_SEQ NOCACHE;

CREATE OR REPLACE PROCEDURE TABLE1_INSERT_PROC (
  p_name  IN  VARCHAR2,
  p_id    OUT NUMBER
)
IS
  wk_id NUMBER(38);
BEGIN
  SELECT ID_SEQ.NEXTVAL INTO wk_id FROM DUAL;
  INSERT INTO TABLE1 ( ID, NAME ) 
    VALUES ( wk_id, p_name ) 
    RETURNING ID INTO p_id;
END;
/

以下のように、動的SQLを使用して一旦順序を削除/作成してからプロシージャを実行するようなPL/SQLを実行した場合、

DECLARE
  wk_id NUMBER(38);
BEGIN
  EXECUTE IMMEDIATE 'DROP SEQUENCE ID_SEQ';
  EXECUTE IMMEDIATE 'CREATE SEQUENCE ID_SEQ NOCACHE';
  TABLE1_INSERT_PROC ( 'name1', wk_id );
  DBMS_OUTPUT.PUT_LINE ( 'id = ' || wk_id );
END;
/

以下のようなエラーが発生します。

行1でエラーが発生しました。:
ORA-04068: パッケージの既存状態は廃棄されました。
ORA-04064: 実行されませんでした。procedure
"SCOTT.TABLE1_INSERT_PROC"は無効になりました
ORA-06508: PL/SQL: コールしているプログラム単位が見つかりませんでした
ORA-06512: 行6

PL/SQLブロックはブロック全体を解析してから実行されます。

そのため、順序が再作成されたことにより、それを利用しているプロシージャも変更されて解析時と実行時で互換性が取れなくなり、上記のエラーが発生するようです。

このエラーを避けるには、以下のようにDDL文を別ブロックに切り出すか、

DROP SEQUENCE ID_SEQ
/
CREATE SEQUENCE ID_SEQ NOCACHE
/
DECLARE
  wk_id NUMBER(38);
BEGIN
  TABLE1_INSERT_PROC ( 'name1', wk_id );
  DBMS_OUTPUT.PUT_LINE ( 'id = ' || wk_id );
END;
/

または、プロシージャの実行にも動的SQLを使用するという方法が考えられます。

DECLARE
  wk_id NUMBER(38);
BEGIN
  EXECUTE IMMEDIATE 'DROP SEQUENCE ID_SEQ';
  EXECUTE IMMEDIATE 'CREATE SEQUENCE ID_SEQ NOCACHE';
  EXECUTE IMMEDIATE 'BEGIN TABLE1_INSERT_PROC ( :1, :2 ); END;' 
    USING 'name1', OUT wk_id;
  DBMS_OUTPUT.PUT_LINE ( 'id = ' || wk_id );
END;
/

ただ、DDL文は実行すると自動的にコミットされてしまうので、このようなプログラムでエラーや障害が発生した場合、データの整合性を損なう危険性もあります。

故によっぽど必要に迫られない限りは、DDL文とDML文を混在させるようなプログラムは避けた方がいいでしょう。



【関連エントリ】
[Oracle] 動的SQLの実行(その1) 2005/01/18
[Oracle] 動的SQLの実行(その2) 2005/01/19
[Oracle] 動的SQLでDDL文を実行しているPL/SQLでORA-6508エラーが発生 2005/02/15