ORA-01002: fetch out of sequence は、Oracleでカーソルの FETCH 順序や状態が崩れた時に発生するエラーです。代表例は、FOR UPDATE カーソルで COMMIT した後にFETCHを続けるケース、カーソルの状態を変えた後に古い順序のまま読み続けるケース、アプリ側でResultSetやREF CURSORの読み取り順序が崩れるケースです。
Oracle公式のORA-01002説明でも、FETCHが正しい順序で実行されていないこと、またはカーソルの状態とFETCH操作が合っていないことが原因として示されています。そのため、対処では OPEN、FETCH、COMMIT、CLOSE の順序を確認します。
ORA-01002が出たら、まず
FOR UPDATE カーソル中に COMMIT していないか確認します。次に、同じカーソルを閉じた後・再OPEN後・アプリ側でclose後にFETCHしていないかを確認します。無効カーソル操作なら ORA-01001、二重OPENなら ORA-06511 側です。ORA-01002とは
ORA-01002は、カーソルのFETCHが想定された順序で実行できない状態を示します。カーソル自体が存在しないというより、FETCHを続ける前提が崩れていると考えると切り分けやすいです。
| 状態 | 操作 | 起きやすいエラー |
|---|---|---|
| 未OPENまたはCLOSE済み | FETCH |
ORA-01001 |
| OPEN済み | 順番にFETCH |
正常 |
| FOR UPDATE中にCOMMIT後 | さらにFETCH |
ORA-01002 |
| OPEN済みを再OPEN | OPEN |
ORA-06511 |
FOR UPDATEカーソル中にCOMMITする例
もっとも実務で見かけるのが、FOR UPDATE カーソルで行をロックしながら処理し、ループ中で COMMIT してしまうケースです。COMMITによりカーソルの前提が崩れ、その後のFETCHでORA-01002になります。
DECLARE
CURSOR c_emp IS
SELECT employee_id
FROM employees
WHERE department_id = 10
FOR UPDATE;
l_id employees.employee_id%TYPE;
BEGIN
OPEN c_emp;
LOOP
FETCH c_emp INTO l_id;
EXIT WHEN c_emp%NOTFOUND;
UPDATE employees
SET last_update_date = SYSDATE
WHERE CURRENT OF c_emp;
COMMIT;
END LOOP;
CLOSE c_emp;
END;
/
-- COMMIT後の次FETCHでORA-01002になりやすい
この場合は、ループ中でCOMMITせず、処理単位の最後でまとめてCOMMITするか、処理設計を分割します。
基本の直し方
まずは、FETCH中にカーソル状態を変える操作をしていないか確認します。特に FOR UPDATE、COMMIT、ROLLBACK、CLOSE、再OPENの順序が重要です。
DECLARE
CURSOR c_emp IS
SELECT employee_id
FROM employees
WHERE department_id = 10
FOR UPDATE;
l_id employees.employee_id%TYPE;
BEGIN
OPEN c_emp;
LOOP
FETCH c_emp INTO l_id;
EXIT WHEN c_emp%NOTFOUND;
UPDATE employees
SET last_update_date = SYSDATE
WHERE CURRENT OF c_emp;
END LOOP;
CLOSE c_emp;
COMMIT;
END;
/
| 確認すること | NG例 | 修正方針 |
|---|---|---|
| FOR UPDATE中のCOMMIT | ループ内COMMIT | ループ後にCOMMIT |
| CLOSE後のFETCH | CLOSE後に続けてFETCH |
処理を終了、または再OPENからやり直す |
| 再OPEN後の古いFETCH | 状態をリセットしたのに古い前提で読む | OPEN/FETCH/CLOSEの単位を分ける |
| アプリ側の読み取り | ResultSet close後にnext | close順序を見直す |
症状別の最短修正
ORA-01002は、どの操作の後にFETCHしているかを見れば原因を絞れます。次の表で、該当する症状から確認します。
| 症状 | 原因になりやすい箇所 | 最短修正 |
|---|---|---|
| ループ途中で突然落ちる | FOR UPDATE中にCOMMIT |
COMMITをループ外へ移す |
| FETCH直後ではなく次のFETCHで落ちる | 直前でカーソル状態を変えた | CLOSE/COMMIT/ROLLBACKを確認 |
| アプリ側の読み取りで落ちる | ResultSetやCursorを閉じた後に読む | close順序と接続寿命を見直す |
| REF CURSORで落ちる | 返却後の読み取り順序や接続管理が崩れている | 読み終わるまでカーソルと接続を維持 |
| ORA-01001と迷う | カーソルが無効か、FETCH順序が崩れたか | OPEN状態と直前操作を確認 |
ORA-01001との違い
ORA-01001とORA-01002はどちらもカーソル系ですが、少し違います。ORA-01001は無効なカーソルを操作した状態、ORA-01002はFETCHの順序や前提が崩れた状態です。
| エラー | 主な意味 | 例 | 関連記事 |
|---|---|---|---|
ORA-01001 |
無効なカーソルを操作 | OPEN前/CLOSE後にFETCH | ORA-01001 |
ORA-01002 |
FETCH順序が不正 | FOR UPDATE中にCOMMIT後FETCH | この記事 |
ORA-06511 |
OPEN済みカーソルを再OPEN | OPEN c; OPEN c; |
ORA-06511 |
ORA-01000 |
開いているカーソル数が多すぎる | カーソル閉じ忘れが蓄積 | ORA-01000 |
カーソルFORループの場合
単純な全件処理なら、カーソルFORループにすることでOPEN/FETCH/CLOSEの順序ミスを減らせます。ただし、FOR UPDATE とトランザクション制御を組み合わせる場合は、COMMIT位置に注意します。
BEGIN
FOR r IN (
SELECT employee_id
FROM employees
WHERE department_id = 10
FOR UPDATE
) LOOP
UPDATE employees
SET last_update_date = SYSDATE
WHERE employee_id = r.employee_id;
END LOOP;
COMMIT;
END;
/
カーソルFORループと明示的カーソルの使い分けは PL/SQLカーソル完全ガイド、明示的カーソルの基本は 明示的カーソル完全ガイド が参考になります。
REF CURSORやアプリ側で起きる場合
Java、C#、Python、PHPなどのアプリケーションからREF CURSORやResultSetを読む場合も、読み取り順序とclose順序が崩れるとORA-01002につながります。DB側が返したカーソルを読み終える前に接続を閉じていないか、途中でトランザクションを終了していないかを確認します。
| 確認項目 | 理由 |
|---|---|
| ResultSet/Cursorを読み終える前にcloseしていないか | 以降のFETCHができなくなる |
| 接続やトランザクションを途中で終了していないか | カーソル状態が変わることがある |
| 複数スレッドで同じカーソルを読んでいないか | FETCH順序が崩れる |
| 例外時のfinally/deferで早く閉じていないか | 後続処理が閉じたカーソルを読む |
JDBCや接続プール経由で発生する場合は、アプリのコードだけでなく接続設定も確認します。autocommit が有効な状態で FOR UPDATE カーソルを読み続けると、アプリ側では明示的に COMMIT していないつもりでも、FETCHの前提が崩れることがあります。
| アプリ側の確認点 | 見る場所 | 直し方 |
|---|---|---|
| autocommitが有効になっていないか | JDBC設定、接続プール、フレームワーク設定 | 読み取り中は明示的なトランザクション境界にする |
| ResultSetを読み切る前にcommit/closeしていないか | finally、try-with-resources、defer、例外処理 | 読み取り完了後にclose/commitする |
| 同じCursorを複数スレッドで読んでいないか | 非同期処理、バッチ並列化 | Cursorを共有せず処理単位を分ける |
| 長時間カーソルを保持していないか | バッチ処理、画面ページング | IDリスト化やページング条件で再取得する |
FETCH across COMMITを避ける
大量処理で「少しずつFETCHして、途中でCOMMITしたい」と考えることがあります。しかし、FOR UPDATE カーソルではCOMMIT後にFETCHを続ける設計は危険です。処理対象を一度IDリストとして確定する、処理単位を分割する、ステータス列で対象を管理するなどの設計にします。
DECLARE
TYPE t_id_list IS TABLE OF employees.employee_id%TYPE;
l_ids t_id_list;
BEGIN
SELECT employee_id
BULK COLLECT INTO l_ids
FROM employees
WHERE department_id = 10;
FOR i IN 1 .. l_ids.COUNT LOOP
UPDATE employees
SET last_update_date = SYSDATE
WHERE employee_id = l_ids(i);
END LOOP;
COMMIT;
END;
/
BULK COLLECT を使う場合は、BULK COLLECT / FORALL完全ガイド も参考になります。
調査手順
PL/SQLでORA-01002が出た場合は、ORA-06512の行番号から USER_SOURCE で該当FETCH行を確認します。そのFETCHの前に、COMMIT、ROLLBACK、CLOSE、再OPEN、例外ハンドラがないかを確認します。
SELECT line,
text
FROM user_source
WHERE name = 'PKG_EMP'
AND type = 'PACKAGE BODY'
AND line BETWEEN 60 AND 100
ORDER BY line;
行番号の読み方は ORA-06512の原因と読み方、バックトレースのログは DBMS_UTILITY完全ガイド が参考になります。
よくある原因と対処一覧
| 原因 | 症状 | 対処 |
|---|---|---|
| FOR UPDATE中のCOMMIT | 次のFETCHでORA-01002 | COMMITをループ外へ移す |
| CLOSE後のFETCH | FETCHで失敗 | 処理を終了、または再OPENからやり直す |
| ROLLBACK後のFETCH | トランザクション終了後に失敗 | FETCH中にトランザクションを終えない |
| アプリ側の読み取り順序ミス | ResultSet読み取り中に失敗 | close/commit/connectionの順序を確認 |
| カーソル状態管理が複雑 | ORA-01001やORA-06511も混在 | カーソルFORループや設計分割を検討 |
チェックリスト
| 項目 | OKの状態 |
|---|---|
| FOR UPDATE中にCOMMITしていない | FETCH終了後にCOMMITしている |
| FETCH前にカーソル状態を変えていない | CLOSE/ROLLBACK/再OPENがない |
| アプリ側で早くcloseしていない | 読み終わってから閉じている |
| ORA-01001と切り分けた | 無効カーソルではなくFETCH順序の問題と分かる |
| ORA-06512の行を確認した | 失敗したFETCH行が分かっている |
よくある質問
ORA-01001とは何が違いますか?
ORA-01001は無効なカーソル操作、ORA-01002はFETCH順序やトランザクションによりFETCHの前提が崩れた状態です。
FOR UPDATE中にCOMMITしてはいけませんか?
カーソルでFETCHを続ける前提なら避けます。COMMITはFETCH終了後に行うか、処理設計を分割します。
アプリ側でだけORA-01002になります
ResultSet/Cursorの読み取り順序、close、commit、connection終了のタイミングを確認してください。
カーソルFORループなら防げますか?
OPEN/FETCH/CLOSEの順序ミスは減らせます。ただしFOR UPDATE中のCOMMITなど、トランザクション制御には引き続き注意が必要です。
まとめ
ORA-01002は、カーソルのFETCH順序や状態が崩れた時に発生します。特に FOR UPDATE カーソル中の COMMIT、CLOSE後のFETCH、アプリ側のResultSet/REF CURSOR読み取り順序に注意します。
対処は、FETCH中にCOMMITやCLOSEを挟まない、COMMITはループ後に行う、カーソル状態を単純に保つことです。ORA-01001やORA-06511との違いを意識して、失敗したFETCH行と直前操作を確認すると切り分けやすくなります。
参考
ORA-01002 – Oracle Database Error Help
Cursors Overview – Oracle Database PL/SQL Language Reference
Cursor Attributes – Oracle PL/SQL User's Guide and Reference
