ERROR: column reference "id" is ambiguous は、JOIN した2つ以上のテーブルに同じ名前の列があり、どのテーブルの列かを書かなかったときのエラーです。 MySQL では ERROR 1052 (23000): Column 'id' in field list is ambiguous になります。

直し方は、列名の前にテーブル名(または別名)を付けることです。

-- エラー
SELECT id, name, amount FROM amb_c c JOIN amb_o o ON o.customer_id = c.id;
-- 直したもの
SELECT c.id, c.name, o.amount FROM amb_c c JOIN amb_o o ON o.customer_id = c.id;

ただし、PL/pgSQL の関数では、テーブルどうしではなく 関数の引数・戻り値の列名と、テーブルの列名がぶつかって、このエラーになります。下で詳しく説明します。

確認環境:PostgreSQL 16.15、MySQL 8.0.46(確認日 2026年10月11日)。顧客(amb_c:id・name・created_at)と注文(amb_o:id・customer_id・amount・created_at)のテーブルで再現しました。

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「column reference is ambiguousの直し方|SQL」

再現:どちらにもある列(id・created_at)

-- PostgreSQL
ERROR:  column reference "id" is ambiguous
LINE 1: SELECT id, name, amount FROM amb_c c JOIN amb_o o ON o.custo...
               ^

-- MySQL
ERROR 1052 (23000): Column 'id' in field list is ambiguous

PostgreSQL では ^ が、MySQL では in field list・in where clause などが、場所を示します。 id のように分かりやすい列だけでなく、created_at・updated_at・name・status など、多くのテーブルに付けている列でよく起きます。

-- WHERE で created_at(どちらのテーブルにもある)
PostgreSQL: ERROR:  column reference "created_at" is ambiguous
MySQL:      ERROR 1052 (23000): Column 'created_at' in where clause is ambiguous

テーブルにあとから列を足したら、今まで動いていた SQL がこのエラーになることがあります。注文テーブルに created_at を足したら、顧客の created_at で絞っていた SQL が止まった、という形です。JOIN する SQL では、すべての列に別名を付けておくと防げます。

ORDER BY・GROUP BY は、DB によって結果が違う

SELECT に c.id だけを書き、ORDER BY・GROUP BY には id とだけ書いたときの結果です。

SQL PostgreSQL 16 MySQL 8.0
SELECT c.id, o.id ... ORDER BY id ORDER BY "id" is ambiguous Column 'id' in order clause is ambiguous
SELECT c.id ... ORDER BY id 動く(SELECT の id を使う) 動く
SELECT c.id, count(*) ... GROUP BY id column reference "id" is ambiguous 動く

ORDER BY は SELECT に書いた列名を優先するので、SELECT に id が1つだけなら動きました(PostgreSQL の公式ドキュメントでも、ORDER BY は出力の列名を使うとされています)。一方、GROUP BY は、PostgreSQL ではエラー、MySQL では動きました。 MySQL から PostgreSQL へ移すと、ここで止まります。ORDER BY・GROUP BY にも別名を付けるのが確実です。

UPDATE の JOIN でも出る

mysql> UPDATE amb_c c JOIN amb_o o ON o.customer_id = c.id SET name = 'x', created_at = '2026-10-11';
ERROR 1052 (23000): Column 'created_at' in field list is ambiguous

更新する列にも別名が要ります(SET c.created_at = ...)。どちらのテーブルを更新するつもりかを、別名で明示します。

USING と NATURAL JOIN

書き方 動き
JOIN ... USING (id) id が1つの列にまとまり、SELECT id と書いてもエラーにならない
NATURAL JOIN 同じ名前の列すべてで結合する
SELECT count(*) FROM amb_c NATURAL JOIN amb_o;
 count
-------
     0

NATURAL JOIN は、エラーにはなりませんが、id と created_at の両方で結合し、0 件になりました(PostgreSQL・MySQL とも)。顧客の id と注文の id は別のものです。曖昧さを避けるために NATURAL JOIN にするのは逆効果で、ON に結合の条件を書くのが安全です。

PL/pgSQL:関数の引数・戻り値の名前と、列名がぶつかる

CREATE FUNCTION get_c(p int) RETURNS TABLE (id int, name text) LANGUAGE plpgsql AS $$
BEGIN
  RETURN QUERY SELECT id, name FROM amb_c WHERE id = p;
END $$;
SELECT * FROM get_c(1);
ERROR:  column reference "id" is ambiguous
LINE 1: SELECT id, name FROM amb_c WHERE id = p
               ^
DETAIL:  It could refer to either a PL/pgSQL variable or a table column.

テーブルは1つしか使っていないのに、エラーになりました。 RETURNS TABLE (id int, name text) の id は、関数の中では変数として扱われ、テーブルの列 id と区別できないためです。DETAIL の a PL/pgSQL variable or a table column が目印です。PL/pgSQL は、既定ではこの場合にエラーにします(公式ドキュメント)。

直し方 書き方 結果(確認環境)
テーブルに別名を付けて書く SELECT c.id, c.name FROM amb_c c WHERE c.id = p 動いた(おすすめ)
#variable_conflict use_column 関数の先頭に書くと、列を優先する 動いた
変数の名前を変える 戻り値を out_id などに(列と重ならない名前) 戻り値の列名も変わる
CREATE FUNCTION get_c3(p int) RETURNS TABLE (id int, name text) LANGUAGE plpgsql AS $$
#variable_conflict use_column
BEGIN
  RETURN QUERY SELECT id, name FROM amb_c WHERE id = p;
END $$;

ON CONFLICT (id) でも出る

CREATE FUNCTION up_c(p int, n text) RETURNS TABLE (id int) LANGUAGE plpgsql AS $$
BEGIN
  RETURN QUERY INSERT INTO amb_c (id, name) VALUES (p, n)
    ON CONFLICT (id) DO UPDATE SET name = n RETURNING amb_c.id;
END $$;
ERROR:  column reference "id" is ambiguous
LINE 1: ...T INTO amb_c (id, name) VALUES (p, n) ON CONFLICT (id) DO UP...
                                                             ^

ON CONFLICT (id) の id は別名を付けられないので、上の直し方1が使えません。 確認環境では、制約の名前で指定すると動きました。

INSERT INTO amb_c AS t (id, name) VALUES (p, n)
ON CONFLICT ON CONSTRAINT amb_c_pkey DO UPDATE SET name = n
RETURNING t.id;

#variable_conflict use_column を関数の先頭に書く方法でも避けられます。UPSERT の書き方全体は、関連記事の「SQL UPSERTの書き方|PostgreSQL・MySQL・MERGE構文比較表」で扱っています。

同じ内容を LANGUAGE sql の関数で書いたときは、エラーにならず動きました。 PL/pgSQL に書き換えたら出るようになった、という場合はこの違いです。

確認の手順(チェックリスト)

順番 確認すること 方法
1 どの列か エラー文の "id"・'id' と、^・in ... clause
2 どのテーブルにその列があるか JOIN しているテーブルを順に見る
3 別名を付ける SELECT・WHERE・ORDER BY・GROUP BY・UPDATE の SET
4 DETAIL に PL/pgSQL variable 関数の引数・RETURNS TABLE の名前と、列名がぶつかっている
5 関数の中 テーブルに別名、#variable_conflict use_column、ON CONFLICT ON CONSTRAINT
6 NATURAL JOIN を使っていないか 同じ名前の列すべてで結合し、0 件になることがある

よくある質問

Q. SQLSTATE は何ですか。 A. PostgreSQL は 42702(ambiguous_column)、MySQL は 23000 です(エラー番号は 1052)。

Q. 同じ名前の列を、両方とも結果に出したいときは。 A. SELECT o.id AS order_id, c.id AS customer_id のように、列に別の名前を付けます。確認環境でも、order_id・customer_id の2列で取得できました。

まとめ

  • JOIN したテーブルに同じ名前の列があり、テーブルを書かなかったエラー
  • 列名の前に別名を付ける(c.id)。created_at など共通の列名で起きやすい
  • 列を足すと、今まで動いていた SQL が止まることがある。JOIN する SQL は全部の列に別名を
  • GROUP BY id は PostgreSQL ではエラー、MySQL では動く
  • PL/pgSQL では、RETURNS TABLE の名前と列名がぶつかる。別名、#variable_conflict use_column、ON CONFLICT ON CONSTRAINT
  • NATURAL JOIN は、意図しない列でも結合する

参考・出典

確認日はいずれも 2026年10月11日です。

  • PostgreSQL 16 Documentation「PL/pgSQL under the Hood」(Variable Substitution):https://www.postgresql.org/docs/16/plpgsql-implementation.html
  • PostgreSQL 16 Documentation「Sorting Rows (ORDER BY)」:https://www.postgresql.org/docs/16/queries-order.html

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「column reference is ambiguousの直し方|SQL」

株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。

WE ARE HIRING

RJCで一緒に開発しながら、
成長を楽しみませんか?

RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!

  • 127日年間休日
  • 12時間平均残業時間
  • 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!

ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。

ABOUT RJC RJCがどんな会社か知る 考え方、研修、働き方、福利厚生、社員の前職まで。RJCのことが丸わかり! RJC丸わかりページへ JOB DESCRIPTION 仕事内容・待遇を見てみる 仕事内容、給与・待遇、選考の流れ。経験者も未経験も!応募前に知りたいこと、まとめました! 募集要項を見る ENTRY エントリーする エントリーは1〜2分・履歴書不要です。まずは話を聞いてみたい、という方でも歓迎です! エントリーフォームへ

RJCで一緒に開発しながら、 成長を楽しみませんか?