ORA-01001: invalid cursor は、Oracleで無効なカーソルを操作した時に発生するエラーです。PL/SQLでは、まだ OPEN していないカーソルを FETCH した、すでに CLOSE したカーソルを再度操作した、カーソル属性を不正な状態で参照した、といったケースで起きます。
Oracle公式のORA-01001説明では、無効なカーソル番号や存在しないカーソルが指定されたことが原因として説明されています。PL/SQL側では、OPEN、FETCH、CLOSE の順序と、カーソルが開いているかを確認するのが基本です。
ORA-01001が出たら、該当行で
FETCH、CLOSE、%FOUND、%NOTFOUND、%ROWCOUNT を使う前に、対象カーソルが OPEN 済みかを確認します。開いているカーソルをもう一度OPENした場合は ORA-06511 側です。ORA-01001とは
ORA-01001は、カーソルが有効な状態ではないのに操作したことを示します。明示的カーソルやカーソル変数では、状態に応じて使える操作が変わります。
| 状態 | 操作 | 結果 |
|---|---|---|
| 未OPEN | FETCH c INTO ... |
ORA-01001 |
| OPEN済み | FETCH c INTO ... |
正常 |
| CLOSE済み | FETCH c INTO ... |
ORA-01001 |
| OPEN済み | OPEN c |
ORA-06511 |
Oracle公式のPL/SQLカーソル説明でも、明示的カーソルはOPEN、FETCH、CLOSEの流れで扱い、開いていないカーソルに対して一部の属性や操作を行うと INVALID_CURSOR 例外になります。
発生する典型例
次の例では、カーソルを OPEN せずに FETCH しています。カーソルが有効な実行状態にないため、ORA-01001になります。
DECLARE
CURSOR c_emp IS
SELECT employee_id
FROM employees;
l_id employees.employee_id%TYPE;
BEGIN
FETCH c_emp INTO l_id;
END;
/
-- ORA-01001: invalid cursor
明示的カーソルの基本は 明示的カーソル完全ガイド にもまとめています。
CLOSE後にFETCHするケース
カーソルを一度閉じた後に、同じカーソルからFETCHしようとしてもORA-01001になります。CLOSE後に再利用する場合は、再度 OPEN してからFETCHします。
DECLARE
CURSOR c_emp IS
SELECT employee_id
FROM employees;
l_id employees.employee_id%TYPE;
BEGIN
OPEN c_emp;
CLOSE c_emp;
FETCH c_emp INTO l_id;
END;
/
-- CLOSE後なのでORA-01001
基本の直し方
カーソルを手動で扱う場合は、OPEN してから FETCH し、最後に CLOSE します。状態が分からない場合は %ISOPEN で確認します。
DECLARE
CURSOR c_emp IS
SELECT employee_id
FROM employees;
l_id employees.employee_id%TYPE;
BEGIN
OPEN c_emp;
LOOP
FETCH c_emp INTO l_id;
EXIT WHEN c_emp%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(l_id);
END LOOP;
CLOSE c_emp;
END;
/
| 操作 | 前提 | 注意点 |
|---|---|---|
OPEN |
カーソルが閉じている | OPEN済みならORA-06511 |
FETCH |
カーソルが開いている | 未OPEN/CLOSE済みならORA-01001 |
%NOTFOUND |
FETCH後に使う | 未OPENではINVALID_CURSORになり得る |
CLOSE |
カーソルが開いている | 二重CLOSEを避けるなら%ISOPEN確認 |
操作別の最短修正
ORA-01001は、どの操作で落ちたかを見ると早く切り分けられます。エラー行が FETCH なのか、CLOSE なのか、属性参照なのかをまず確認します。
| 落ちた操作 | よくある原因 | 最短修正 |
|---|---|---|
FETCH |
OPEN前、またはCLOSE後にFETCHしている | FETCH前にOPENし、CLOSE後は再FETCHしない |
CLOSE |
すでに閉じているカーソルを閉じている | %ISOPEN確認後にCLOSE |
%FOUND |
OPEN/FETCH前に参照している | FETCH後に参照する |
%NOTFOUND |
OPEN/FETCH前に参照している | FETCH直後の終了判定に使う |
%ROWCOUNT |
カーソルが有効でない | OPEN後のFETCH回数として読む |
症状別の最短修正
ORA-01001は、カーソルの状態と操作の順序を見ると切り分けやすいです。発生行の操作ごとに、次のように確認します。
| 症状 | 原因になりやすい箇所 | 最短修正 |
|---|---|---|
FETCHで失敗 |
OPEN前またはCLOSE後にFETCHしている | OPEN済みか確認し、順序を直す |
%FOUNDや%NOTFOUNDで失敗 |
カーソルが開いていない | FETCH後に属性を参照する |
CLOSEで失敗 |
すでに閉じているカーソルを閉じている | %ISOPENを確認してからCLOSE |
| 2回目の処理で失敗 | 例外時の状態管理が崩れている | 例外ハンドラで状態を整理する |
| アプリ側だけで失敗 | ResultSet/Cursorを閉じた後に読んでいる | クローズ順序とトランザクション境界を確認 |
カーソル属性で起きる場合
%FOUND、%NOTFOUND、%ROWCOUNT は便利ですが、カーソルが正しい状態でないとエラーになります。%ISOPEN は状態確認に使いやすい属性です。
DECLARE
CURSOR c_emp IS
SELECT employee_id
FROM employees;
BEGIN
IF c_emp%NOTFOUND THEN
DBMS_OUTPUT.PUT_LINE('no rows');
END IF;
END;
/
-- OPENもFETCHもしていないため不正
DECLARE
CURSOR c_emp IS
SELECT employee_id
FROM employees;
l_id employees.employee_id%TYPE;
BEGIN
OPEN c_emp;
FETCH c_emp INTO l_id;
IF c_emp%NOTFOUND THEN
DBMS_OUTPUT.PUT_LINE('no rows');
END IF;
CLOSE c_emp;
END;
/
ORA-06511との違い
ORA-01001とORA-06511はどちらもカーソル状態の問題ですが、逆方向のエラーです。ORA-06511は開いているカーソルをまた開いた時、ORA-01001は開いていない・無効なカーソルを操作した時に起きます。
| エラー | 状態 | 例 | 対処 |
|---|---|---|---|
ORA-06511 |
OPEN済みなのにOPEN | OPEN c; OPEN c; |
ORA-06511を確認 |
ORA-01001 |
未OPEN/CLOSE済みなのにFETCHなど | CLOSE c; FETCH c |
OPEN/FETCH/CLOSE順序を直す |
ORA-01000 |
開いているカーソル数が多すぎる | カーソル閉じ忘れが蓄積 | ORA-01000を確認 |
ORA-01002 |
FETCH順序が不正 | COMMIT後FETCHなど | 次の記事候補として切り分け |
カーソルFORループに置き換える
手動のOPEN/FETCH/CLOSEが不要なら、カーソルFORループに置き換えると状態管理のミスを減らせます。PL/SQLがOPEN、FETCH、CLOSEを管理するため、ORA-01001やORA-06511の原因を減らしやすいです。
BEGIN
FOR r IN (
SELECT employee_id
FROM employees
) LOOP
DBMS_OUTPUT.PUT_LINE(r.employee_id);
END LOOP;
END;
/
カーソルFORループと明示的カーソルの使い分けは PL/SQLカーソル完全ガイド も参考になります。
REF CURSORやアプリ側で起きる場合
REF CURSORをアプリケーションへ返す処理では、DB側・アプリ側のどちらで閉じるかが曖昧だとORA-01001につながることがあります。Java、C#、Python、PHPなどでは、ResultSetやCursorを閉じた後に読み続けていないか、接続やトランザクションの終了でカーソルが無効になっていないかを確認します。
| 確認項目 | 理由 |
|---|---|
| ResultSet/Cursorを閉じた後に読んでいないか | 閉じたカーソルは無効になる |
| 接続を閉じた後に読み続けていないか | 接続終了でカーソルも使えなくなる |
| COMMIT/ROLLBACKのタイミング | カーソル状態に影響する処理がある |
| 例外時のfinally/defer処理 | 早すぎるcloseで後続読み取りが失敗する |
REF CURSORを返す側・読む側の責任分界
REF CURSORを返す設計では、DB側はカーソルを開いて返し、呼び出し側は読み終わってから閉じる、という責任分界を明確にします。返す前に閉じたり、呼び出し側が読む前に接続を閉じたりすると、ORA-01001につながりやすくなります。
CREATE OR REPLACE PROCEDURE p_get_emps(p_cur OUT SYS_REFCURSOR) IS
BEGIN
OPEN p_cur FOR
SELECT employee_id, last_name
FROM employees;
END;
/
-- 呼び出し側が読み終わるまで、接続やカーソルを閉じるタイミングに注意する
| 担当 | やること | 注意点 |
|---|---|---|
| DB側 | REF CURSORをOPENして返す | 返す前に閉じない |
| アプリ側 | 最後まで読み取る | 読み取り前にcloseしない |
| アプリ側 | 読み終わったらcloseする | 閉じ忘れはORA-01000の原因にもなる |
| 共通 | 例外時の後始末を決める | 早すぎるcloseと閉じ忘れの両方を避ける |
例外処理で順序が崩れる場合
例外ハンドラでカーソルを閉じた後、外側の処理が同じカーソルをFETCHし続けるとORA-01001になり得ます。例外時は状態を整理し、必要なら処理を中断します。
DECLARE
CURSOR c_emp IS
SELECT employee_id
FROM employees;
l_id employees.employee_id%TYPE;
BEGIN
OPEN c_emp;
FETCH c_emp INTO l_id;
CLOSE c_emp;
EXCEPTION
WHEN OTHERS THEN
IF c_emp%ISOPEN THEN
CLOSE c_emp;
END IF;
RAISE;
END;
/
例外処理の設計は PL/SQL例外処理ガイド も参考になります。
調査手順
PL/SQL内でORA-01001が出た場合は、ORA-06512の行番号から USER_SOURCE で該当行を確認します。その行がFETCHなのか、CLOSEなのか、カーソル属性参照なのかを見て、直前のOPEN/CLOSE順序を追います。
SELECT line,
text
FROM user_source
WHERE name = 'PKG_EMP'
AND type = 'PACKAGE BODY'
AND line BETWEEN 60 AND 90
ORDER BY line;
発生行の読み方は ORA-06512の原因と読み方、バックトレースのログは DBMS_UTILITY完全ガイド が参考になります。
よくある原因と対処一覧
| 原因 | 症状 | 対処 |
|---|---|---|
| OPEN前にFETCH | 最初のFETCHでORA-01001 | OPENしてからFETCH |
| CLOSE後にFETCH | CLOSE後の処理で失敗 | 再OPENするか処理を終了 |
| 属性参照の順序ミス | %NOTFOUNDなどで失敗 |
FETCH後に参照 |
| アプリ側で先にclose | ResultSet/Cursor読み取り時に失敗 | close順序を見直す |
| 手動管理が複雑 | 正常系・例外系で状態がずれる | カーソルFORループへ置換 |
チェックリスト
| 項目 | OKの状態 |
|---|---|
| FETCH前にOPENした | 未OPENカーソルを読んでいない |
| CLOSE後にFETCHしていない | 閉じたカーソルを再利用していない |
| 属性参照の順序が正しい | FETCH後に%FOUND/%NOTFOUNDを見ている |
| 例外時のclose順序を確認した | 後続処理が閉じたカーソルを触らない |
| ORA-06512の行を確認した | 失敗した操作が分かっている |
よくある質問
ORA-01001とORA-06511は何が違いますか?
ORA-01001は無効なカーソルを操作した時、ORA-06511はOPEN済みカーソルを再度OPENした時です。
%ISOPENを見れば防げますか?
状態確認には有効です。ただし、FETCHや属性参照の順序が間違っている場合は、OPEN/FETCH/CLOSEの流れ自体を直します。
カーソルFORループなら安全ですか?
手動OPEN/FETCH/CLOSEが不要になるため、状態管理ミスは減らせます。単純な全件処理では有力な選択肢です。
アプリ側でだけORA-01001になります
ResultSet/Cursor、接続、トランザクションを閉じるタイミングを確認してください。DBから返したカーソルを読み終える前に閉じている可能性があります。
まとめ
ORA-01001は、無効なカーソルや閉じたカーソルを操作した時に発生します。OPEN前のFETCH、CLOSE後のFETCH、カーソル属性参照の順序ミス、アプリ側で閉じた後の読み取りが主な原因です。
対処は、OPEN/FETCH/CLOSEの順序を整理し、必要なら %ISOPEN で状態を確認することです。単純な処理ならカーソルFORループへ置き換えると、カーソル状態の管理ミスを減らせます。
参考
ORA-01001 – Oracle Database Error Help
Cursors Overview – Oracle Database PL/SQL Language Reference
Cursor Attributes – Oracle PL/SQL User's Guide and Reference

