【Oracle】ORA-00918の原因と解決方法|列の定義があいまいです

ORA-00918: column ambiguously defined は、JOINしている複数の表に同じ列名があり、SQL内でどちらの列を使うのかOracleが判断できない時に発生するエラーです。たとえば employeesdepartments の両方に department_id があり、SELECT department_id とだけ書くと曖昧になります。

解決方法は基本的に明快で、列名を 表別名.列名 の形で指定します。ただし、実際にはSELECT句だけでなく、WHERE句、ORDER BY句、サブクエリ、ビュー作成、SQL生成処理でも起きるため、エラー箇所を探す順番を知っておくと修正が速くなります。

先に結論
JOINした表に同名列がある場合は、曖昧になりうるすべての参照を e.department_idd.department_id のように表別名付きで書きます。長いSQLでは、SELECT句だけでなく WHEREORDER BYGROUP BY、インラインビューの出力列名まで確認します。
スポンサーリンク

ORA-00918とは

Oracle公式の説明では、JOINで使われる列名が複数の表に存在し、修飾なしで参照された時にORA-00918が発生します。対処は、同名列の参照へ表名または表別名とピリオドを付けて、どの表の列なのか明示することです。

JOINの基本や複数表結合の組み立て方を整理したい場合は、JOIN完全ガイド も参照してください。列別名・表別名の基本は AS句で別名を設定する方法 にまとめています。

エラー文に列名と表名が表示された場合

ORA-00918の表示形式によっては、曖昧な列名と、その列が存在する2つの表名までエラー文に表示されます。たとえば DEPARTMENT_ID: column ambiguously specified - appears in EMPLOYEES and DEPARTMENTS のように出た場合は、SQL内の修飾なしの department_id を優先して探します。

read-new-error-message.txt
ORA-00918: DEPARTMENT_ID: column ambiguously specified -
appears in EMPLOYEES and DEPARTMENTS

-- 修正対象を探す観点
-- SELECT department_id      -> SELECT e.department_id
-- WHERE department_id = :id  -> WHERE e.department_id = :id
-- ORDER BY department_id     -> ORDER BY e.department_id

環境によっては従来どおり column ambiguously defined だけが表示されます。その場合は、結合表の定義とSQL内の列参照を照合して候補を絞ります。

最小の再現例と直し方

次のSQLでは、employee_idemployee_name は一方の表だけにあるとしても、department_id が両方の表に存在すると、SELECT句の参照が曖昧になります。

bad-ambiguous-column.sql
SELECT employee_id,
       department_id,
       department_name
FROM employees e
JOIN departments d
  ON e.department_id = d.department_id;

どちらの department_id を表示したいか指定すると解決します。実務では、曖昧な列だけではなく、読みやすさのために取得列へ一貫して表別名を付けるのがおすすめです。

fixed-qualified-columns.sql
SELECT e.employee_id,
       e.department_id,
       d.department_name
FROM employees e
JOIN departments d
  ON e.department_id = d.department_id;

発生しやすい場所

SELECT句

同名列を取得する時に表別名がないケースです。JOINを追加したあと、もともと単一表向けだったSELECT句が曖昧になることがあります。

WHERE句

絞り込み条件の statusidcreated_at などが複数表に存在し、どちらで絞るか指定されていないケースです。

ORDER BY / GROUP BY句

表示列は修正したものの、並び順や集計キーに修飾なしの同名列が残っているケースです。

サブクエリやビュー

内側のSELECTが同名の出力列を返し、外側で参照する段階やビュー作成時に列名が衝突するケースです。列別名を明示します。

WHERE句で発生する例

両方の表に status 列がある場合、WHERE句でも表別名が必要です。どちらのステータスで絞り込むかは結果の意味にも影響するため、機械的な修正ではなく意図を確認します。

bad-where-column.sql
SELECT e.employee_id,
       d.department_name
FROM employees e
JOIN departments d
  ON e.department_id = d.department_id
WHERE status = 'ACTIVE';
fixed-where-column.sql
SELECT e.employee_id,
       d.department_name
FROM employees e
JOIN departments d
  ON e.department_id = d.department_id
WHERE e.status = 'ACTIVE'
  AND d.status = 'ACTIVE';

ORDER BYやGROUP BYで発生する例

SELECT句を直した後もエラーが続く場合、並び替えや集計の列指定に同名列が残っていないか確認します。集計SQLで列指定に問題がある場合は、ORA-00979と取り違えないように注意します。

bad-order-by-column.sql
SELECT e.employee_id,
       e.created_at AS employee_created_at,
       d.created_at AS department_created_at
FROM employees e
JOIN departments d
  ON e.department_id = d.department_id
ORDER BY created_at DESC;
fixed-order-by-column.sql
SELECT e.employee_id,
       e.created_at AS employee_created_at,
       d.created_at AS department_created_at
FROM employees e
JOIN departments d
  ON e.department_id = d.department_id
ORDER BY e.created_at DESC;

集計時の列指定エラーについては、ORA-00979の原因と解決方法 も確認してください。

インラインビューやビューでの同名列

JOINした結果をインラインビューやビューとして扱う場合は、出力列の名前も重複しないようにしておくと安全です。両方の表から同名列を出したい場合は、意味が分かる列別名を付けます。

avoid-duplicate-output-columns.sql
SELECT x.employee_id,
       x.employee_department_id,
       x.department_master_id
FROM (
  SELECT e.employee_id,
         e.department_id AS employee_department_id,
         d.department_id AS department_master_id
  FROM employees e
  JOIN departments d
    ON e.department_id = d.department_id
) x;

サブクエリの種類や使い分けは サブクエリ完全ガイド が参考になります。

SELECT * を避ける理由

複数表をJOINして SELECT * を使うと、同名列が結果セットに複数含まれます。単純な表示では動く場合があっても、そのSQLをビュー化したり、外側のSELECTで参照したり、アプリ側で列名マッピングしたりすると問題が見えやすくなります。実務では必要な列だけを列挙し、重複する列には意味のある別名を付ける方が安全です。

explicit-select-list.sql
SELECT e.employee_id,
       e.employee_name,
       e.department_id AS employee_department_id,
       d.department_name,
       d.department_id AS department_master_id
FROM employees e
JOIN departments d
  ON e.department_id = d.department_id;

USING句やNATURAL JOINを使っている場合

JOIN ... USING (department_id) を使うと、結合に使った共通列は結果上1つの列として扱われます。この場合は SELECT department_id のように参照できますが、e.department_id のように表別名で修飾すると、ORA-00918ではなく別のエラーになることがあります。

using-clause-example.sql
SELECT e.employee_id,
       department_id,
       d.department_name
FROM employees e
JOIN departments d
USING (department_id);
JOINの書き方を混在させない
ON で結合するSQLは e.department_id のように修飾し、USING の共通列は修飾せず参照します。USING句の共通列を修飾すると ORA-25154、NATURAL JOINの共通列を修飾すると ORA-25155 の原因になります。既存SQLを直す時は、列修飾だけでなく結合構文そのものを確認してください。
bad-qualified-using-column.sql
SELECT e.employee_id,
       e.department_id,  -- USING句の共通列を修飾すると ORA-25154 の原因
       d.department_name
FROM employees e
JOIN departments d
USING (department_id);

どの列が重複しているか調べるSQL

結合表が多いSQLでは、同じ名前の列を目視で探すのが大変です。ALL_TAB_COLUMNS を使うと、対象表間で重複する列名を一覧化できます。

find-duplicate-column-names.sql
SELECT column_name,
       COUNT(*) AS table_count,
       LISTAGG(table_name, ', ') WITHIN GROUP (ORDER BY table_name) AS tables
FROM all_tab_columns
WHERE owner = UPPER('&OWNER')
  AND table_name IN ('EMPLOYEES', 'DEPARTMENTS', 'LOCATIONS')
GROUP BY column_name
HAVING COUNT(*) > 1
ORDER BY column_name;

ここで見つかった列がSQL内で修飾なしに使われていないか、SELECT句、JOIN条件、WHERE句、ORDER BY句、GROUP BY句の順で確認します。

ORMやBIツールの生成SQLで発生した場合

アプリケーションやBIツールでは、画面上にSQLが見えなくても、JOIN追加や検索条件の変更後にORA-00918が出ることがあります。この場合は、まず実行されたSQL本文を取得し、同名列の修飾漏れを探します。SQL本文の確認方法は V$SQLAREA・V$SQLTEXTとは も参考になります。

find-failing-sql-text.sql
SELECT sql_id,
       parsing_schema_name,
       last_active_time,
       sql_fulltext
FROM v$sql
WHERE UPPER(sql_text) LIKE '%' || UPPER('&TABLE_NAME') || '%'
ORDER BY last_active_time DESC
FETCH FIRST 20 ROWS ONLY;

取得したSQLがビューや共通テーブル式を重ねている場合は、外側のSELECTだけでなく、内側のSELECTリストで同名の出力列を作っていないかも確認します。修正できない製品SQLであれば、ビュー側で列名を一意にする対応が選択肢になります。

ORA-00904との違い

ORA-00918

列は存在しますが、複数表に同名列があり、どちらを参照するか曖昧です。表別名を付けて解決します。

ORA-00904

指定した列名や識別子が存在しない、別名の使い方が誤っている、引用符や予約語に問題がある場合に発生します。詳細は ORA-00904の原因と解決方法 を参照してください。

ORA-00979

GROUP BYを使う集計SQLで、SELECT句の非集計列がGROUP BYに含まれていない場合などに発生します。

修正のチェック手順

  1. SQLでJOINしている表を列挙する
  2. エラーになった列名が複数表に存在するか確認する
  3. SELECT句の同名列を 表別名.列名 に修正する
  4. WHERE、ORDER BY、GROUP BY、HAVING、CONNECT BYなどにも曖昧参照が残っていないか確認する
  5. インラインビューやビューの場合は、出力列へ重複しない列別名を付ける
  6. USINGNATURAL JOIN を使っている場合は、共通列の参照方法を確認する
  7. SQL生成ツールやORMが生成したSQLなら、実際に実行されたSQLを取得して修正箇所を特定する

よくある質問

JOIN条件に表別名を付けているのに、なぜORA-00918になりますか?

JOIN条件だけを直しても、SELECT句、WHERE句、ORDER BY句などに修飾なしの同名列が残っていれば発生します。SQL全体で同じ列名を検索してください。

列名が同じなら、常にORA-00918になりますか?

同名列が存在するだけで必ず失敗するわけではありません。その列を曖昧な形で参照した時にエラーになります。ただし、後から条件や表示列を追加した際に発生しやすいので、JOIN SQLでは最初から表別名を付けると安全です。

表名を毎回書く必要がありますか?

長い表名を繰り返す必要はありません。FROM employees e のように短い表別名を付け、e.employee_id のように参照します。

まとめ

ORA-00918は、JOINした複数表に同じ列名があり、参照元を特定できない時に発生します。修正の基本は、曖昧な列を 表別名.列名 で明示することです。

SELECT句だけを直して終わらせず、WHERE句、ORDER BY句、GROUP BY句、サブクエリやビューの出力列まで確認してください。また、必要な列を明示して意味のある列別名を付ける習慣は、ORA-00918の防止だけでなくSQLの保守性向上にもつながります。

参考