【Oracle】ORA-06533の原因と解決方法|Subscript beyond count の直し方

【Oracle】ORA-06533の原因と解決方法|Subscript beyond count の直し方 Oracle

ORA-06533: Subscript beyond count は、Oracle PL/SQLのコレクションで、存在しない要素番号を参照・代入した時に発生するエラーです。ネスト表やVARRAYを初期化していても、EXTEND で要素を増やす前に l_list(1) へ代入したり、COUNT=0 の状態で1件目を読んだりすると発生します。

Oracle公式のORA-06533説明では、添字がVARRAYの要素数を超えている、またはネスト表に対して大きすぎることが原因で、必要に応じて明示的に EXTEND することが対処として示されています。

先に結論
ORA-06533が出たら、まず COUNT と参照している添字を確認します。要素を追加したいなら EXTEND してから代入し、読み取りたいなら COUNT > 0EXISTS で確認します。未初期化なら ORA-06531、初期化済みで要素数不足ならORA-06533です。
スポンサーリンク

ORA-06533とは

ORA-06533は、PL/SQLコレクションの添字が、現在保持している要素数の範囲を超えた時に出ます。コレクション自体は初期化済みであっても、要素がまだ作られていない場合は参照できません。

状態 起きやすいエラー
未初期化 l_ids t_num_list; のまま使う ORA-06531
初期化済み・0件 l_ids := t_num_list(); l_ids(1) ORA-06533
初期化済み・1件 l_ids(2) を参照 ORA-06533
要素追加済み EXTEND 後に範囲内を参照 正常

発生する典型例

次の例では、ネスト表を空で初期化していますが、要素を追加していません。そのため l_ids(1) へ代入しようとした時点でORA-06533になります。

ora06533-no-extend.sql
DECLARE
  TYPE t_num_list IS TABLE OF NUMBER;
  l_ids t_num_list := t_num_list();
BEGIN
  l_ids(1) := 100;
END;
/

-- ORA-06533: Subscript beyond count
-- ORA-06512: at line 5

このケースは未初期化ではありません。初期化はできていますが、要素数が0件のまま1件目に触っているのが原因です。ORA-06512の行番号の読み方は ORA-06512の原因と読み方 も参考になります。

基本の直し方

要素を追加してから代入する場合は、先に EXTEND します。最後に追加した要素へ代入するなら、l_list(l_list.COUNT) を使うと添字ずれを避けやすいです。

extend-before-assign.sql
DECLARE
  TYPE t_num_list IS TABLE OF NUMBER;
  l_ids t_num_list := t_num_list();
BEGIN
  l_ids.EXTEND;
  l_ids(l_ids.COUNT) := 100;

  DBMS_OUTPUT.PUT_LINE(l_ids(1));
END;
/
やりたいこと 安全な書き方 理由
1件追加する EXTENDしてl_list(COUNT)へ代入 追加位置に確実に入れられる
複数件追加する EXTEND(n)後に範囲内へ代入 先に枠を作る
1件目を読む IF l_list.COUNT > 0 THEN 0件時の参照を避ける
特定添字を読む IF l_list.EXISTS(i) THEN 削除済み要素にも対応しやすい

症状別の最短修正

ORA-06533は、代入で出ているのか、読み取りで出ているのかによって直し方が変わります。まずは発生行の操作を見て、次のように切り分けます。

症状 原因になりやすい箇所 最短修正
l_list(1) := valueで失敗 要素追加前に代入している EXTENDしてからl_list(COUNT)へ代入
l_list(1)の読み取りで失敗 COUNT=0なのに1件目を読んでいる COUNT > 0を確認
ループ中に途中で失敗 固定上限や別変数の件数で回している 1 .. l_list.COUNTを使う
FIRST .. LASTで失敗 DELETE後の穴あき要素を読んでいる EXISTS(i)を確認
BULK COLLECT後に失敗 取得0件または想定件数未満 参照前にCOUNTを見て分岐

ORA-06531との違い

ORA-06531とORA-06533は、どちらもコレクションで出ますが原因が違います。切り分けでは、まずコレクション自体が初期化済みか、その次に要素数が足りているかを見ます。

エラー 状態 確認すること 対処
ORA-06531 コレクションがNULL コンストラクタを呼んだか t_list() で初期化
ORA-06533 初期化済みだが要素数不足 COUNT と添字 EXTEND または範囲チェック
ORA-06502 値や型が合わない 文字列長、数値変換、代入先型 ORA-06502を確認

未初期化コレクションの詳細は ORA-06531の原因と解決方法 で整理しています。PL/SQLコレクション全体の基礎は PL/SQLコレクション型完全ガイド も合わせて確認してください。

COUNTを使った範囲チェック

単純に1から COUNT まで詰まっているネスト表やVARRAYなら、1 .. l_list.COUNT のループが使いやすいです。ただし、DELETE で途中の要素を削除している場合は、EXISTS も使います。

safe-count-loop.sql
DECLARE
  TYPE t_num_list IS TABLE OF NUMBER;
  l_ids t_num_list := t_num_list(10, 20, 30);
BEGIN
  FOR i IN 1 .. l_ids.COUNT LOOP
    DBMS_OUTPUT.PUT_LINE(l_ids(i));
  END LOOP;
END;
/
safe-exists-check.sql
DECLARE
  TYPE t_num_list IS TABLE OF NUMBER;
  l_ids t_num_list := t_num_list(10, 20, 30);
BEGIN
  l_ids.DELETE(2);

  FOR i IN l_ids.FIRST .. l_ids.LAST LOOP
    IF l_ids.EXISTS(i) THEN
      DBMS_OUTPUT.PUT_LINE(l_ids(i));
    END IF;
  END LOOP;
END;
/

FIRST / LASTを使う時の注意

FIRSTLAST は、最初と最後の添字を返す便利なメソッドです。ただし、空のコレクションでは NULL を返すため、そのままループ範囲に使うと別のエラーや意図しない動きにつながります。

safe-first-last-loop.sql
DECLARE
  TYPE t_num_list IS TABLE OF NUMBER;
  l_ids t_num_list := t_num_list();
BEGIN
  IF l_ids.COUNT > 0 THEN
    FOR i IN l_ids.FIRST .. l_ids.LAST LOOP
      IF l_ids.EXISTS(i) THEN
        DBMS_OUTPUT.PUT_LINE(l_ids(i));
      END IF;
    END LOOP;
  END IF;
END;
/

Oracle公式のコレクションメソッド説明でも、COUNTFIRSTLASTEXTEND などはコレクションの状態確認と操作に使うメソッドとして整理されています。

BULK COLLECTで0件だった場合

BULK COLLECT で取得したコレクションは、0件のことがあります。その直後に l_rows(1) を読むとORA-06533になりやすいので、必ず COUNT を確認します。大量データ処理は BULK COLLECT / FORALL完全ガイド も参考になります。

bulk-collect-zero-row.sql
DECLARE
  TYPE t_num_list IS TABLE OF NUMBER;
  l_ids t_num_list;
BEGIN
  SELECT employee_id
  BULK COLLECT INTO l_ids
  FROM employees
  WHERE department_id = -1;

  IF l_ids.COUNT > 0 THEN
    DBMS_OUTPUT.PUT_LINE(l_ids(1));
  END IF;
END;
/

明示的カーソルや BULK COLLECT LIMIT を使う場合は、明示的カーソル完全ガイド のように、取得件数ごとの処理終了条件を明確にします。

FORALLで添字がずれる場合

FORALL では、ループ範囲とコレクションの実在する添字が合っていないと、添字関連のエラーにつながります。削除済み要素や疎なコレクションを扱う場合は、INDICES OFVALUES OF の利用を検討します。

forall-indices-of.sql
DECLARE
  TYPE t_num_list IS TABLE OF NUMBER;
  l_ids t_num_list := t_num_list(10, 20, 30);
BEGIN
  l_ids.DELETE(2);

  FORALL i IN INDICES OF l_ids
    INSERT INTO work_ids(id) VALUES (l_ids(i));
END;
/

大量DMLでは、単に 1 .. l_ids.COUNT と書くより、実在する添字を対象にする方が安全な場面があります。FORALL と例外処理は BULK COLLECT / FORALL完全ガイド と合わせて確認してください。

VARRAYで出る場合

VARRAYでは、現在の要素数を超える添字に代入するとORA-06533になります。また、最大要素数を超えて増やそうとする場合は別の添字関連エラーになることがあります。COUNTLIMIT の両方を見ます。

varray-count-limit.sql
DECLARE
  TYPE t_code_list IS VARRAY(3) OF VARCHAR2(10);
  l_codes t_code_list := t_code_list();
BEGIN
  l_codes.EXTEND;
  l_codes(1) := 'A001';

  DBMS_OUTPUT.PUT_LINE('COUNT=' || l_codes.COUNT || ', LIMIT=' || l_codes.LIMIT);
END;
/

DELETE後の穴あきコレクション

ネスト表では DELETE によって途中の要素が欠けることがあります。COUNT は要素数、LAST は最後の添字なので、必ずしも同じ値とは限りません。穴あきの可能性がある場合は EXISTS を使います。

sparse-nested-table.sql
DECLARE
  TYPE t_num_list IS TABLE OF NUMBER;
  l_ids t_num_list := t_num_list(10, 20, 30);
BEGIN
  l_ids.DELETE(2);

  DBMS_OUTPUT.PUT_LINE('COUNT=' || l_ids.COUNT); -- 2
  DBMS_OUTPUT.PUT_LINE('LAST=' || l_ids.LAST);   -- 3

  IF l_ids.EXISTS(2) THEN
    DBMS_OUTPUT.PUT_LINE(l_ids(2));
  END IF;
END;
/

調査手順

実際にORA-06533が出たら、ORA-06512の行番号から、USER_SOURCE でどの添字を参照しているかを確認します。その行の直前で COUNTEXTENDDELETEBULK COLLECT の結果を見ます。

check-user-source-ora06533.sql
SELECT line,
       text
FROM user_source
WHERE name = 'PKG_ORDER'
  AND type = 'PACKAGE BODY'
  AND line BETWEEN 80 AND 100
ORDER BY line;
順番 見る場所 確認内容
1 ORA-06512の最初のオブジェクト付き行 どのソース行で添字参照したか
2 参照している添字 i や固定値がCOUNT以内か
3 直前のEXTEND 要素を増やしてから代入しているか
4 BULK COLLECT結果 0件時の防御があるか
5 DELETEの有無 穴あきにEXISTSで対応しているか

よくある原因と対処一覧

原因 症状 対処
EXTENDせずに代入 l_list(1) := valueで失敗 先にEXTEND
0件なのに1件目を読む BULK COLLECT後にl_list(1)で失敗 COUNT > 0を確認
ループ範囲がずれている 1 .. n のnがCOUNTより大きい 1 .. l_list.COUNTにする
DELETEで穴あき FIRST .. LAST内で欠番を読む EXISTS(i)を確認
VARRAYの件数不足 現在件数を超えた添字へ代入 COUNTLIMITを確認

チェックリスト

項目 OKの状態
コレクションを初期化した ORA-06531ではない状態になっている
代入前にEXTENDした 代入先の添字が存在している
読み取り前にCOUNTを見た 0件時に要素参照していない
穴あきならEXISTSを見た DELETE後の欠番を読んでいない
BULK COLLECT後の0件を処理した 取得0件でも落ちない
ORA-06512の行を確認した 発生行と呼び出し元を分けている

よくある質問

初期化しているのにORA-06533になります

初期化はできていても、要素数が足りない可能性があります。COUNT を確認し、代入前に EXTEND してください。

ORA-06531との違いは何ですか?

ORA-06531はコレクション自体がNULL、ORA-06533は初期化済みだが添字が要素数を超えている状態です。

FIRST .. LASTでループしているのに落ちます

DELETEで穴あきになっている可能性があります。ループ内で EXISTS(i) を確認してください。

BULK COLLECTで0件の時はどうすればよいですか?

COUNT > 0 を確認してから1件目を参照します。0件なら処理をスキップするか、空結果として扱います。

まとめ

ORA-06533は、PL/SQLコレクションの要素数を超えて添字参照した時に発生します。未初期化のORA-06531とは違い、コレクション自体は初期化済みで、要素数や添字の扱いに問題がある状態です。

対処は、要素追加前に EXTEND する、読み取り前に COUNT を確認する、穴あきコレクションでは EXISTS を使う、BULK COLLECT の0件を考慮する、という流れです。ORA-06512の行番号から実際に範囲外参照している箇所を確認すると、修正箇所を絞り込みやすくなります。

参考

ORA-06533 – Oracle Database Error Help

Collection Methods – Oracle Database PL/SQL Language Reference

PL/SQL Collections and Records – Oracle Database PL/SQL Language Reference