- PR -

動的SQL使用時にCOMMITすると次のフェッチでエラー

1
投稿者投稿内容
HASSHII
会議室デビュー日: 2005/11/28
投稿数: 3
投稿日時: 2005-11-28 13:43
対象テーブルが不定(引数でもらう)なので動的SQL文を作成してフェッチを行い、
項目に対してチェックを行って、エラーとなる場合にはそのテーブルの
区分にエラー値を設定したいのですが、COMMITを発行した後、次のフェッチを行うと
ORA-01002: フェッチ順序が無効です。
が発生します。処理の都合上、1レコード毎にCOMMITしたいのですが、
動的SQLを使用する場合は無理なのでしょうか。別の方法があれば教えて下さい。
コード:
 w_SQL := 'SELECT ' || 'ROWID, ';
 w_SQL := w_SQL  || 'SHORI_MODE, SHORI_KBN FROM ' || w_TableName;
 w_SQL := w_SQL  || ' WHERE SHORI_KBN = ''' || SYRKBN_MI || '''';
 w_SQL := w_SQL  || ' FOR UPDATE';

 w_CUR_SQL := DBMS_SQL.OPEN_CURSOR;
 DBMS_SQL.PARSE(w_CUR_SQL, w_SQL, DBMS_SQL.NATIVE);

 w_REC_NUM(1)  := NULL;
 DBMS_SQL.DEFINE_COLUMN(w_CUR_SQL, 1, w_REC_NUM(1));
 w_REC_CHR(2)  := NULL;
 DBMS_SQL.DEFINE_COLUMN(w_CUR_SQL, 2, w_REC_CHR(2));

 w_SQLExec := DBMS_SQL.EXECUTE(w_CUR_SQL);

 LOOP
  w_SQLFetch := DBMS_SQL.FETCH_ROWS(w_CUR_SQL);
  EXIT WHEN w_SQLFetch < 1;

  DBMS_SQL.COLUMN_VALUE(w_CUR_SQL, 1, w_REC_NUM(1));
  DBMS_SQL.COLUMN_VALUE(w_CUR_SQL, 2, w_REC_CHR(2));

  チェック

  IF w_Error  = TRUE THEN
   w_SQL_UP := NULL;
   w_SQL_UP := w_SQL_UP || 'UPDATE ' || w_TableName;
   w_SQL_UP := w_SQL_UP || ' SET SHORI_KBN = ''' || C_KBN_ERR || '''';
   w_SQL_UP := w_SQL_UP || ' WHERE ROWID = ''' ||  w_ROWID || '''';
   w_CUR_SQL_UP := DBMS_SQL.OPEN_CURSOR;
   DBMS_SQL.PARSE(w_CUR_SQL_UP, w_SQL_UP, DBMS_SQL.NATIVE);
   w_SQLExec_UP := DBMS_SQL.EXECUTE(w_CUR_SQL_UP);
   COMMIT;
   DBMS_SQL.CLOSE_CURSOR(w_CUR_SQL_UP);
  END IF;

 END LOOP;


宜しくお願いします。
今川 美保(夏椰)
ぬし
会議室デビュー日: 2004/06/10
投稿数: 363
お住まい・勤務地: 神奈川県茅ヶ崎市
投稿日時: 2005-11-28 16:23
動的SQLでなく、静的な埋め込みSQLでも
同じエラーメッセージが出力されます。

1件ずつCommitしたいとのことですが、
SELECT文でFOR UPDATEをつけているので
すべての処理が終わるまでCOMMITしないほうがいいんじゃないでしょうか?

FOR UPDATEのロックが外れてもいいのであれば、BULK FETCHという方法で
1件Commitは出来るようになるのですが・・・・。

コード:

DECLARE
CURSOR c1 IS SELECT col1 FROM test.test1 for update nowait ;
TYPE COLList IS TABLE OF test.test1.col1%TYPE;
colvals COLList ;
row_num number := 1;
BEGIN
OPEN c1;
FETCH c1 BULK COLLECT INTO colvals;
CLOSE c1 ;
LOOP
dbms_output.put_line(colvals(row_num)) ;
insert into test.test values(colvals(row_num)) ;
commit work;
row_num := row_num + 1 ;
exit when row_num > colvals.last;
END LOOP ;
END;
/



#カーソルクローズ忘れたので追記

[ メッセージ編集済み 編集者: 夏椰【SUICA】 編集日時 2005-11-28 16:32 ]
HASSHII
会議室デビュー日: 2005/11/28
投稿数: 3
投稿日時: 2005-11-28 17:17
FOR UPDATEが問題の様です。
1件ずつCOMMITする必要がある場合はFOR UPDATEを付けてはならない様ですね。

せっかく教えて頂いたBULKですが、テーブル名が不定なためTABLE OFでの定義は
難しいようです。
業務的にロックの必要性の有無をもう一度検討したいと思います。

有難う御座いました。
今川 美保(夏椰)
ぬし
会議室デビュー日: 2004/06/10
投稿数: 363
お住まい・勤務地: 神奈川県茅ヶ崎市
投稿日時: 2005-11-28 19:38
引用:

HASSHIIさんの書き込み (2005-11-28 17:17) より:

せっかく教えて頂いたBULKですが、テーブル名が不定なためTABLE OFでの定義は
難しいようです。



あ〜と、ごめんなさい。例が悪かったようで。
コード:
TYPE COLList IS TABLE OF test.test1.col1%TYPE;


の「TEST.TEST1.COL1%TYPE」は列の型を指定しているのです。
なのでTEST.TEST1.COL1がVARCHAR(100)だったとしたら
コード:
TYPE COLList IS TABLE OF VARCHAR(100);


と書いても同じ動作となります。

よって、更新する列の型がわかっているのであれば、
その型を書いていただければいいかと。

以上、補足でした(^^;
HASSHII
会議室デビュー日: 2005/11/28
投稿数: 3
投稿日時: 2005-11-29 10:38
実は取得する項目名、項目数も別情報(別テーブルで指定)なのです。
よって、実行時にならないとどの型の変数がいくつ必要になるか判らないのです。
NUMBER型の索引付き表w_REC_NUMやCHAR型の索引付き表w_REC_CHR等を作って
何とか取り出そうとしているのです。なので教えて頂いたcol1の為の変数や
TABLE OF VARCHAR(100);という指定は難しいのです。

今回は表全体をロックする必要はなかった様なのでFOR UPDATEを除くだけで良くなりそうです。

何度も有難う御座いました。
1

スキルアップ/キャリアアップ(JOB@IT)