ORA-06504: PL/SQL: Return types of Result Set variables or query do not match は、REF CURSOR や FETCH INTO で、SELECT結果の列構成と受け取り側の変数・レコード型が合わない時に発生するエラーです。列数が違う、列の順番が違う、型が違う、SELECT * の結果が変わった、というケースでよく起きます。
似たカーソル系エラーとして ORA-01001 や ORA-01002 がありますが、ORA-06504はカーソルの状態ではなく、カーソルから返る行の形と受け取り側の形が合っていないことが中心です。
ORA-06504が出たら、まず
OPEN ... FOR SELECT のSELECT列と、FETCH ... INTO の変数・レコードの数、順番、型を見比べます。SELECT * は列追加や列順変更で壊れやすいため、明示的な列指定、%ROWTYPE、専用レコード型に寄せます。発生行は ORA-06512 の行番号から追います。ORA-06504とは
ORA-06504は、PL/SQLでResult Setを受け取る変数やレコード型と、実際の問い合わせ結果が一致しない時の実行時エラーです。特に SYS_REFCURSOR は戻り行の形をコンパイル時に強く固定しないため、実行時に初めて不一致が見つかることがあります。
| 不一致の種類 | 例 | 起きやすい場所 |
|---|---|---|
| 列数が違う | SELECTは3列、FETCH先は2変数 | FETCH INTO |
| 列順が違う | 数値列を文字列変数に入れる | レコード型、明示変数 |
| 型が違う | DATEをNUMBERに受ける | 動的SQL、REF CURSOR |
| SELECT * が変わる | 表に列追加、ビュー定義変更 | 保守後、リリース後 |
| 強い型付けカーソルとSELECTが違う | RETURN型とOPEN FORの列が違う | パッケージAPI |
Oracle公式のORA-06504説明でも、Result Setの変数または問い合わせの戻り型が一致しないことが原因として示されています。つまり、対処では「返す側」と「受ける側」の行構造を揃えることが最優先です。
列数が合わない基本例
もっとも分かりやすいのは、カーソルが返す列数と FETCH INTO の受け取り変数の数が違うケースです。
DECLARE
l_cur SYS_REFCURSOR;
l_empno employees.employee_id%TYPE;
l_name employees.first_name%TYPE;
BEGIN
OPEN l_cur FOR
SELECT employee_id, first_name, hire_date
FROM employees;
FETCH l_cur INTO l_empno, l_name;
-- SELECTは3列、FETCH先は2変数なので一致しない
CLOSE l_cur;
END;
/
この場合は、SELECT列を2列にするか、FETCH先を3つに増やします。どちらが正しいかは、呼び出し元が本当に必要としている列に合わせて決めます。
DECLARE
l_cur SYS_REFCURSOR;
l_empno employees.employee_id%TYPE;
l_name employees.first_name%TYPE;
l_hired employees.hire_date%TYPE;
BEGIN
OPEN l_cur FOR
SELECT employee_id, first_name, hire_date
FROM employees;
FETCH l_cur INTO l_empno, l_name, l_hired;
CLOSE l_cur;
END;
/
列の型・順番が合わない例
列数が合っていても、列の順番や型が違うとORA-06504になります。SELECT側の1列目は NUMBER なのに、FETCH先の1つ目が日付やレコードの別項目になっている、といったケースです。
DECLARE
TYPE t_emp_rec IS RECORD (
emp_name employees.first_name%TYPE,
emp_id employees.employee_id%TYPE
);
l_cur SYS_REFCURSOR;
l_rec t_emp_rec;
BEGIN
OPEN l_cur FOR
SELECT employee_id, first_name
FROM employees;
FETCH l_cur INTO l_rec;
-- レコードは name, id の順だが、SELECTは id, name の順
CLOSE l_cur;
END;
/
レコード型へFETCHする場合は、SELECT列の順番とレコード項目の順番を一致させます。名前が同じでも、FETCHでは列名ではなく位置で対応する点に注意します。
| 確認点 | NG例 | 修正 |
|---|---|---|
| 列数 | SELECTが3列、FETCH先が2項目 | 同じ数に揃える |
| 列順 | SELECTはid,name、レコードはname,id | 順番を揃える |
| データ型 | DATEをNUMBERへFETCH | %TYPEや正しい型にする |
| NULL可否 | NULLを想定しない後続処理 | 戻り値の意味を決める |
| 暗黙変換 | 文字列日付をDATE扱い | 明示変換または型を揃える |
SELECT * が危険な理由
ORA-06504でよくあるのが、SELECT * を返すREF CURSORです。表やビューに列が追加されると、FETCH先のレコード型と列数・列順が変わり、以前は動いていたコードが急に落ちることがあります。
OPEN l_cur FOR SELECT * FROM employees; -- employees に列が追加されると、呼び出し側のFETCH先とズレることがある
外部へ返すカーソルやパッケージAPIでは、SELECT * を避けて列を明示します。APIとして返す列を固定できるため、呼び出し側との契約が壊れにくくなります。
OPEN l_cur FOR
SELECT employee_id,
first_name,
hire_date
FROM employees;
-- 返す列を固定する
%ROWTYPEで揃える
表やカーソルの行構造をそのまま受けるなら、%ROWTYPE を使うと列構造を揃えやすくなります。ただし、%ROWTYPE でもSELECT列の構造と一致していることが前提です。
DECLARE
CURSOR c_emp IS
SELECT employee_id, first_name, hire_date
FROM employees;
l_rec c_emp%ROWTYPE;
BEGIN
OPEN c_emp;
FETCH c_emp INTO l_rec;
CLOSE c_emp;
END;
/
%ROWTYPE や %TYPE の使い方は PL/SQL変数・定数ガイド、基本的なカーソルの流れは 明示的カーソルガイド も参考になります。
SYS_REFCURSORと強い型付けREF CURSOR
SYS_REFCURSOR は柔軟ですが、どんな行構造が返るかを呼び出し側が把握していないと不一致が起きやすくなります。一方、強い型付けREF CURSORは戻り型を宣言できるため、APIの契約を明確にしやすいです。
| 種類 | 特徴 | ORA-06504対策 |
|---|---|---|
SYS_REFCURSOR |
戻り行の形が柔軟 | 列仕様をコメントやAPI仕様で固定する |
| 弱い型付けREF CURSOR | 任意のSELECTをOPENできる | 呼び出し側と列構造を共有する |
| 強い型付けREF CURSOR | RETURN型を宣言する | SELECT列をRETURN型に合わせる |
| 通常カーソル | カーソル定義と%ROWTYPEを使いやすい | PL/SQL内部処理に向く |
DECLARE
TYPE t_emp_cur IS REF CURSOR RETURN employees%ROWTYPE;
l_cur t_emp_cur;
l_rec employees%ROWTYPE;
BEGIN
OPEN l_cur FOR
SELECT *
FROM employees;
FETCH l_cur INTO l_rec;
CLOSE l_cur;
END;
/
REF CURSORの設計全体は REF CURSORガイド に詳しくまとめています。外部アプリに返す場合は、返す列の契約を固定することが特に重要です。
JDBCやODP.NETから呼ぶ場合
Javaの CallableStatement、.NETの OracleDataReader、PythonやPHPのカーソル取得でREF CURSORを受ける場合も、DB側が返す列仕様とアプリ側が読む列仕様がズレるとORA-06504の原因になります。アプリ側では「何列目に何型が来るか」を前提にしているため、DB側のSELECT列を変更した時は呼び出し側も同時に確認します。
| 確認点 | よくあるズレ | 対処 |
|---|---|---|
| OUTパラメータの種類 | REF CURSORのつもりが別型で受けている | 登録するOUT型を確認する |
| 列の読み取り順 | 1列目を文字列として読むが実際はNUMBER | 列順と型をAPI仕様に書く |
| 列名参照 | 別名を変えてアプリ側が読めない | 列別名を固定する |
| NULLの扱い | NULLを想定しない型変換で落ちる | NULL可否を仕様に含める |
| DB側のSELECT変更 | 列追加や順番変更をアプリに伝えていない | APIバージョンを分ける |
-- APIとして返す列仕様を固定する
OPEN p_result FOR
SELECT employee_id AS employee_id,
first_name AS first_name,
hire_date AS hire_date
FROM employees
WHERE department_id = p_department_id;
-- 呼び出し側は 1: employee_id(NUMBER), 2: first_name(VARCHAR2), 3: hire_date(DATE) として読む
外部アプリ向けのREF CURSORでは、列を増やしたい時に既存のカーソル定義を変えるより、新しいプロシージャや新しい戻り列セットとして分ける方が安全なことがあります。特に複数アプリから呼ばれているパッケージAPIでは、列数・列順・列別名を互換性の一部として扱います。
動的SQLで起きる場合
動的SQLでSELECT句を条件によって変えると、呼び出し側のFETCH先と合わないパターンが生まれやすくなります。検索条件だけを動的にし、返す列は固定する設計にするとORA-06504を防ぎやすくなります。
OPEN l_cur FOR
'SELECT employee_id, first_name, hire_date
FROM employees
WHERE department_id = :dept_id'
USING p_department_id;
-- WHERE句は動的でも、SELECT列は固定する
動的SQLでは、条件によって employee_id, first_name だけ返す経路と、employee_id, first_name, hire_date を返す経路が混ざると危険です。呼び出し側が1つのFETCH先を使うなら、返す列数も1つに固定します。
BULK COLLECTで起きる場合
BULK COLLECT でも、SELECT結果とコレクション要素の型が合っていないとエラーになります。複数列を一括取得するなら、レコード型のコレクションを使うか、列ごとのコレクションを用意します。
DECLARE
TYPE t_emp_rec IS RECORD (
employee_id employees.employee_id%TYPE,
first_name employees.first_name%TYPE,
hire_date employees.hire_date%TYPE
);
TYPE t_emp_tab IS TABLE OF t_emp_rec;
l_emps t_emp_tab;
BEGIN
SELECT employee_id, first_name, hire_date
BULK COLLECT INTO l_emps
FROM employees;
END;
/
BULK COLLECTの基本は BULK COLLECT / FORALLガイド、コレクション型の整理は PL/SQLコレクション型ガイド が参考になります。
ORA-06512から発生行を追う
ORA-06504は、実際には FETCH 行やREF CURSORを返す関数の呼び出し行で見つかることが多いです。エラースタックに ORA-06512 が出ている場合は、行番号から該当するFETCHやOPEN FORを確認します。ソース確認には USER_SOURCE を使います。
ORA-06504: PL/SQL: Return types of Result Set variables or query do not match ORA-06512: at "APP.PKG_EMP", line 42 ORA-06512: at line 1
SELECT line,
text
FROM user_source
WHERE name = 'PKG_EMP'
AND type = 'PACKAGE BODY'
AND line BETWEEN 35 AND 50
ORDER BY line;
行番号の読み方は ORA-06512の読み方 を使うと整理しやすいです。カーソルが閉じている、FETCH順序がおかしい、といった状態の問題なら ORA-01001 や ORA-01002 側も確認します。
症状別の直し方
| 症状 | 原因候補 | 直し方 |
|---|---|---|
| FETCH行で落ちる | SELECT列とFETCH先が不一致 | 列数・順番・型を揃える |
| リリース後に急に落ちる | SELECT * の対象表やビューが変わった | 明示列にする |
| 特定の条件だけ落ちる | 動的SQLのSELECT句が条件で変わる | 返す列構造を固定する |
| 外部アプリから呼ぶと落ちる | REF CURSORの列仕様が共有されていない | API仕様として列名・型・順番を固定する |
| BULK COLLECTで落ちる | コレクション要素型とSELECT列が不一致 | レコード型または列ごとのコレクションにする |
チェックリスト
| 項目 | OKの状態 |
|---|---|
| SELECT列数とFETCH先の数が同じ | 1対1で対応している |
| SELECT列順とレコード項目順が同じ | 位置で対応しても問題ない |
| 型が一致している | %TYPEや%ROWTYPEでズレを減らしている |
| SELECT * を使っていない | 外部へ返す列は明示している |
| 動的SQLでも返す列構造が固定 | 条件で列数が変わらない |
| ORA-06512の行番号を確認した | FETCH行またはOPEN FOR行が特定できている |
よくある質問
列名が同じなら順番が違っても大丈夫ですか?
FETCHでは基本的に位置で対応します。列名が同じでも、SELECT列の順番と受け取り側の順番が違うと不一致になります。
SYS_REFCURSORを使うと型チェックされないのですか?
柔軟に使える一方で、戻り行の形をコンパイル時に固定しにくいです。そのため、実行時にORA-06504として見つかることがあります。
SELECT * を使ってはいけませんか?
内部の一時的な処理なら使える場面もありますが、REF CURSORで外部へ返すAPIや長期保守するコードでは避ける方が安全です。
ORA-01001やORA-01002との違いは何ですか?
ORA-01001やORA-01002はカーソルの状態やFETCH順序の問題です。ORA-06504は返る行の形と受け取り側の形が合わない問題です。
まとめ
ORA-06504は、REF CURSORやFETCH INTOで、問い合わせ結果と受け取り側の列数・順番・型が一致しない時に発生します。SYS_REFCURSOR、動的SQL、SELECT *、レコード型へのFETCHで特に注意が必要です。
対処では、SELECT列とFETCH先を1列ずつ照合し、必要なら明示列、%TYPE、%ROWTYPE、専用レコード型に揃えます。発生行は ORA-06512 で追い、外部へ返すREF CURSORは列仕様をAPI契約として固定しておくと再発を防ぎやすくなります。
参考
ORA-06504 – Oracle Database Error Help
Cursors Overview – Oracle Database PL/SQL Language Reference
PL/SQL Subprograms – Oracle Database PL/SQL Language Reference
