3

BULK COLLECT を使用して Oracle (11g) でレコードを処理しているときに、興味深い予期しない問題に遭遇しました。

次のコードは問題なく実行され、100 万以上のすべてのレコードを問題なく処理しました。

-- Define cursor
cursor My_Data_Cur Is
Select col1
      ,col2
from My_Table_1;
…

-- Open the cursor
open My_Data_Cur;

-- Loop through all the records in the cursor
loop

  -- Read the first group of records
  fetch My_Data_Cur
  bulk collect into My_Data_Rec
  limit 100;

  -- Exit when there are no more records to process
  Exit when My_Data_Rec.count = 0;

  -- Loop through the records in the group
  for idx in 1 .. My_Data_Rec.count
  loop
    … do work here to populate a records to be inserted into My_Table_2 …
  end loop;

  -- Insert the records into the second table
  forall idx in 1 .. My_Data_Rec.count
  insert into My_Table_2…;

  -- Delete the records just processed from the source table
  forall idx in 1 .. My_Data_Rec.count
  delete from My_Table_1 …;

  commit;
end loop;

100 レコードの各グループ (上限は 100) の処理の最後に、読み取って処理したばかりのレコードを削除するので、カーソル定義に「for update」構文を追加して、別のプロセスが実行できるようにすることをお勧めします。データが読み取られてからレコードが削除されるまでの間、レコードを更新しないでください。

だから、私が変更した唯一のコードは…</p>

cursor My_Data_Cur
is
  select col1
        ,col2
from My_Table_1
for update;

この変更後に PL/SQL パッケージを実行すると、ジョブは 100 レコードしか処理せずに終了します。カーソルから「for update」を削除することで、この変更が問題の原因であることを確認し、もう一度パッケージがソース テーブルのすべてのレコードを処理しました。

「for update」句を追加すると、この動作が変化する理由はありますか? この問題を回避する方法について何か提案はありますか? プロセスの開始時にテーブルで排他的トランザクションを開始しようとしていますが、データを処理するテーブル全体をロックしたくないため、これは解決策ではありません。

よろしくお願いいたします。

許す

4

2 に答える 2

1

ジャスティンの説明に追加。

Exception以下のエラー メッセージが表示されるはずです。ハンドラーがこれを抑制したかどうかはわかりません。

そして、メッセージ自体が多くのことを説明しています!

この種の更新では、メイン テーブルのシャドウ コピーを作成し、パブリック シノニムがそれを指すようにすることをお勧めします。一部のバッチ ID は、メイン テーブルへのプライベート シノニムを作成し、バッチ操作を実行して、メンテナンスをより簡単にします。

Error report -
ORA-01002: fetch out of sequence
ORA-06512: at line 7
01002. 00000 -  "fetch out of sequence"
*Cause:    This error means that a fetch has been attempted from a cursor
           which is no longer valid.  Note that a PL/SQL cursor loop
           implicitly does fetches, and thus may also cause this error.
           There are a number of possible causes for this error, including:
           1) Fetching from a cursor after the last row has been retrieved
           and the ORA-1403 error returned.
           2) If the cursor has been opened with the FOR UPDATE clause,
           fetching after a COMMIT has been issued will return the error.
           3) Rebinding any placeholders in the SQL statement, then issuing
           a fetch before reexecuting the statement.
*Action:   1) Do not issue a fetch statement after the last row has been
           retrieved - there are no more rows to fetch.
           2) Do not issue a COMMIT inside a fetch loop for a cursor
           that has been opened FOR UPDATE.
           3) Reexecute the statement after rebinding, then attempt to
           fetch again.

また、を使用してロジックを変更できますrowid

ドキュメントの例:

DECLARE
-- if "FOR UPDATE OF salary" is included on following line, an error is raised
   CURSOR c1 IS SELECT e.*,rowid FROM employees e;
   emp_rec  employees%ROWTYPE;
BEGIN
   OPEN c1;
   LOOP
     FETCH c1 INTO emp_rec; -- FETCH fails on the second iteration with FOR UPDATE
     EXIT WHEN c1%NOTFOUND;
     IF emp_rec.employee_id = 105 THEN
       UPDATE employees SET salary = salary * 1.05 WHERE rowid = emp_rec.rowid;
         -- this mimics WHERE CURRENT OF c1
     END IF;
     COMMIT;  -- releases locks
   END LOOP;
END;
/

行ごとにレコードを取得する必要があります!! ROWID AND COMMIT を使用してすぐに更新してください。そして、次の行に進みます!

しかし、これにより、オプションを放棄する必要がありBulk Bindingます。

于 2014-02-07T21:31:49.110 に答える