ORA-00904: 無効な識別子です(英語: invalid identifier)は、Oracle開発の現場で日常的に遭遇するエラーです。列名・テーブル名・エイリアスなどの識別子が無効だとOracleが判断したときに発生します。エラーメッセージ自体はシンプルですが、発生パターンは多岐にわたり、特に引用符付き識別子の挙動はベテランでもはまりやすいポイントです。本記事では代表的な発生パターンごとに原因と対処法を体系的に解説します。
- ORA-00904 の発生メカニズムと識別子のルール
- 列名スペルミス・大文字小文字が原因のパターン
- 引用符付き識別子(ダブルクォート)が原因のパターン
- SELECT 列エイリアスを WHERE・GROUP BY・HAVING で使うとエラーになる理由
- テーブルエイリアスの誤用が原因のパターン
- 予約語を列名に使うときの注意点
- INSERT・UPDATE 文での列名ミス
- USER_TAB_COLUMNS・DESCRIBE を使った列名確認方法
ORA-00904 の発生メカニズム
Oracleの識別子とは、列名・テーブル名・ビュー名・エイリアスなどSQL内で参照する名前のことです。Oracleが識別子を認識できないときに ORA-00904 が発生します。
識別子には引用符なし(通常識別子)と引用符あり(引用符付き識別子)の2種類があり、挙動が大きく異なります。
| 種類 | 記述例 | 大文字小文字 | 使用可能文字 |
|---|---|---|---|
| 通常識別子 | EMPLOYEE_ID | 大文字に自動変換 | 英数字・_・$・#(先頭は英字) |
| 引用符付き識別子 | “employee_id” | そのまま保持(区別あり) | ほぼすべての文字(スペース・予約語も可) |
通常識別子はOracleが内部で大文字に変換して管理します。一方、ダブルクォートで囲んだ引用符付き識別子は大文字小文字を区別して保持します。この違いが ORA-00904 の最大の落とし穴になります。
パターン:列名のスペルミス・存在しない列
最も基本的なパターンです。単純なスペルミスや、そもそも存在しない列名を参照すると発生します。
-- employees テーブルに employee_name 列は存在しない(正しくは last_name) SELECT employee_name FROM employees; -- → ORA-00904: "EMPLOYEE_NAME": 無効な識別子です
エラーメッセージに含まれる識別子名をもとに、実際の列名を確認します。
-- テーブルの列名・データ型・桁数を確認する SELECT column_name, data_type, data_length, nullable FROM user_tab_columns WHERE table_name = 'EMPLOYEES' -- テーブル名は大文字で指定 ORDER BY column_id;
DESC employees; -- または DESCRIBE employees;
ポイント:USER_TAB_COLUMNS の table_name は大文字で格納されているため、WHERE条件は大文字で指定します。スキーマをまたいで参照する場合は ALL_TAB_COLUMNS または DBA_TAB_COLUMNS を使います。
パターン:引用符付き識別子の大文字小文字不一致
テーブルや列をダブルクォートで囲んで作成した場合、参照時も同じ大文字小文字で囲む必要があります。これを忘れると ORA-00904 が発生します。
-- 列が "employee_id"(小文字)で作成されている場合
CREATE TABLE test_table (
"employee_id" NUMBER, -- 小文字で作成
name VARCHAR2(100)
);
-- 通常識別子で参照すると大文字に変換される → EMPLOYEE_ID を探すがない
SELECT employee_id FROM test_table;
-- → ORA-00904: "EMPLOYEE_ID": 無効な識別子です
-- ダブルクォートなしで引用符付き列名と同じ綴りを書いても大文字変換で不一致
SELECT Employee_Id FROM test_table;
-- → ORA-00904: "EMPLOYEE_ID": 無効な識別子です
-- "employee_id"(小文字)で作成した列は、参照時も同じ書き方が必要 SELECT "employee_id" FROM test_table; -- 通常識別子(大文字変換)で作成した列は引用符不要 SELECT name FROM test_table; -- NAME と同じ意味
外部ツール(ETLツール・ORMフレームワーク等)が自動生成したDDLにダブルクォートが含まれていることがあります。そのような列を手書きのSQLで参照する際は、必ずダブルクォートと正確な大文字小文字を使ってください。
新規テーブル設計では、ダブルクォートなしで作成できる通常識別子(大文字・アンダースコア)を使うことを強く推奨します。引用符付き識別子は保守性が下がり、ORA-00904 の温床になります。
パターン:SELECT のエイリアスを WHERE・GROUP BY で参照
SELECT 句で付けた列エイリアスを WHERE 句や GROUP BY 句で直接参照するとORA-00904が発生します。これはSQLの評価順序に起因するOracleの仕様です。
-- total_price という名前は SELECT の評価後に決まるが、
-- WHERE 句は SELECT より先に評価される
SELECT product_id,
quantity * unit_price AS total_price
FROM order_items
WHERE total_price >= 10000;
-- → ORA-00904: "TOTAL_PRICE": 無効な識別子です
-- サブクエリの SELECT が先に実行されて total_price が確定する
SELECT *
FROM (
SELECT product_id,
quantity * unit_price AS total_price
FROM order_items
)
WHERE total_price >= 10000;
-- エイリアスを使わずに同じ計算式を WHERE 句に書く
SELECT product_id,
quantity * unit_price AS total_price
FROM order_items
WHERE quantity * unit_price >= 10000;
GROUP BY でのエイリアス参照も同様です。ただし ORDER BY 句はエイリアスを使えます(ORDER BY は SELECT の後に評価されるため)。
-- 評価順序(概念):
-- FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY
--
-- SELECT エイリアスが有効なのは ORDER BY のみ
-- WHERE / GROUP BY / HAVING では使用不可
-- OK: ORDER BY ではエイリアスが使える
SELECT product_id,
quantity * unit_price AS total_price
FROM order_items
ORDER BY total_price DESC; -- ← エイリアス使用可
-- NG: GROUP BY ではエイリアスが使えない
SELECT department_id,
SUM(salary) AS total_salary
FROM employees
GROUP BY total_salary; -- → ORA-00904: "TOTAL_SALARY": 無効な識別子です
-- OK: GROUP BY には元の列または式を書く
SELECT department_id,
SUM(salary) AS total_salary
FROM employees
GROUP BY department_id;
パターン:テーブルエイリアスの誤用
テーブルに付けたエイリアスを間違えると ORA-00904 が発生します。特にサブクエリや複数テーブルJOINで起きやすいパターンです。
-- エイリアス e を定義しているのに、別の箇所で emp と書いてしまっている SELECT e.employee_id, emp.last_name FROM employees e WHERE e.department_id = 10; -- → ORA-00904: "EMP"."LAST_NAME": 無効な識別子です
-- FROM に e と書いたのに SELECT でテーブル名を使おうとしている SELECT employees.last_name, e.salary FROM employees e WHERE e.department_id = 10; -- → ORA-00904: "EMPLOYEES"."LAST_NAME": 無効な識別子です -- エイリアス e を定義した時点でテーブル名 employees は使えなくなる
-- エイリアス e を定義して一貫して使う SELECT e.employee_id, e.last_name, e.salary FROM employees e WHERE e.department_id = 10;
FROM 句でエイリアスを定義すると、そのSQL内では元のテーブル名での参照ができなくなります。一度エイリアスを付けたら、そのSQL全体でエイリアスで統一して参照してください。
パターン:予約語を列名・エイリアスとして使う
OracleのSQL予約語(SELECT・FROM・WHERE・DATE・LEVEL・NUMBER など)を列名やエイリアスとして使おうとすると ORA-00904 が発生することがあります。
-- DATE は予約語 SELECT sysdate AS date FROM dual; -- → ORA-00904 または ORA-00923(文脈による)
-- ダブルクォートで囲むと予約語をエイリアスに使える SELECT sysdate AS "date" FROM dual; -- ただし参照時も引用符が必要なため、なるべく予約語以外の名前を選ぶのが無難 -- 推奨: わかりやすい別名にする SELECT sysdate AS current_date_val FROM dual;
-- 以下はOracleの予約語または特別な意味を持つキーワード -- 列名・エイリアスにそのまま使うと問題が起きやすい -- LEVEL → 階層問い合わせの疑似列 -- ROWNUM → 疑似列 -- ROWID → 疑似列 -- DATE → データ型 -- NUMBER → データ型 -- VARCHAR2 → データ型 -- COMMENT → 予約語 -- TABLE → 予約語 -- FROM → 予約語 -- 確認方法: 以下のビューで予約語一覧を確認 SELECT keyword, reserved FROM v$reserved_words WHERE reserved = 'Y' ORDER BY keyword;
パターン:INSERT・UPDATE 文での列名ミス
INSERT や UPDATE の列名部分に誤りがあると ORA-00904 が発生します。特にORMやアプリケーションから動的に生成されるSQLで見落とされやすいです。
-- テーブルに emp_name という列は存在しない(正しくは last_name) INSERT INTO employees (employee_id, emp_name, department_id) VALUES (999, 'テスト', 10); -- → ORA-00904: "EMP_NAME": 無効な識別子です
-- USER_TAB_COLUMNS で列名を確認してから実行 INSERT INTO employees (employee_id, last_name, department_id) VALUES (999, 'テスト', 10);
-- emplyee_id はスペルミス(正しくは employee_id) UPDATE employees SET emplyee_id = 1000 WHERE employee_id = 999; -- → ORA-00904: "EMPLYEE_ID": 無効な識別子です
パターン:サブクエリ内での外部列の参照範囲外参照
相関サブクエリを使う際、参照できる列のスコープを誤ると ORA-00904 が発生します。
-- d はサブクエリの外にある別のブロックのエイリアス
-- サブクエリ内で参照できないスコープの列を使っている
SELECT e.employee_id, e.last_name
FROM employees e
WHERE e.salary > (
SELECT AVG(salary)
FROM departments d -- ここで定義した d は
WHERE d.department_id = e.department_id -- ← e は外側のエイリアスなので参照可
AND location_id = d.location_id -- ← d はサブクエリ内なので参照可
);
-- WITH 句で定義したエイリアスは、その後の SELECT 内でのみ有効
WITH dept_avg AS (
SELECT department_id, AVG(salary) AS avg_salary
FROM employees
GROUP BY department_id
)
-- dept_avg は以下の SELECT で参照可能
SELECT e.employee_id, e.last_name, d.avg_salary
FROM employees e
JOIN dept_avg d ON e.department_id = d.department_id
WHERE e.salary > d.avg_salary;
列名・テーブル名を確認するSQL
ORA-00904 が発生したら、まず実際のオブジェクト定義を確認することが解決への近道です。
-- 自分のスキーマのテーブル列情報 SELECT column_name, data_type, data_length, data_precision, data_scale, nullable FROM user_tab_columns WHERE table_name = 'テーブル名(大文字)' ORDER BY column_id; -- 他のスキーマのテーブルも含めて確認 SELECT owner, table_name, column_name, data_type FROM all_tab_columns WHERE table_name = 'テーブル名(大文字)' ORDER BY owner, table_name, column_id;
-- 自分のスキーマのテーブル・ビュー一覧
SELECT object_name, object_type, status
FROM user_objects
WHERE object_type IN ('TABLE', 'VIEW')
ORDER BY object_type, object_name;
-- テーブル名があいまいな場合はLIKEで絞り込む
SELECT object_name, object_type
FROM user_objects
WHERE object_name LIKE '%EMP%'
ORDER BY object_name;
-- 列名の実際の格納状態を確認(大文字か小文字か混在か) SELECT column_name FROM user_tab_columns WHERE table_name = 'TEST_TABLE' ORDER BY column_id; -- 結果が 'employee_id'(小文字)なら引用符付き識別子で作られている -- 結果が 'EMPLOYEE_ID'(大文字)なら通常識別子で作られている
ORA-00904 の対処フロー
ORA-00904 が発生したら以下の順番で確認すると効率よく解決できます。
- エラーメッセージの識別子名を確認する(どの識別子が無効か)
- USER_TAB_COLUMNS または DESC で実際の列名を確認する
- 引用符付き識別子チェック:作成時にダブルクォートを使ったか確認。使っていれば参照時も同じ大文字小文字でクォートが必要
- エイリアスのスコープチェック:SELECT 列エイリアスを WHERE/GROUP BY で使っていないか確認
- テーブルエイリアスのチェック:エイリアス名を一貫して使えているか確認
- 予約語チェック:列名・エイリアスにOracleの予約語を使っていないか確認
まとめ
ORA-00904(無効な識別子です)は、識別子(列名・テーブル名・エイリアス)をOracleが認識できないときに発生するエラーです。
発生の多くはスペルミス・引用符付き識別子の大文字小文字不一致・SELECT エイリアスのスコープ誤りの3パターンに集約されます。USER_TAB_COLUMNS や DESC コマンドで実際の列名を確認する習慣を持つことと、引用符付き識別子を使わないシンプルな命名規則を設計時に決めておくことが、再発防止の鍵です。
同じORAエラーシリーズとして、ORA-01722(数値が無効です)やORA-00001(一意制約違反)の解説も参考にしてください。
