PostgreSQL『duplicate column name』エラーの原因と対処
- 作成日 2025.09.02
- 更新日 2025.10.06
- PostgreSQL
- PostgreSQL
CREATE/ALTER/INSERT/UPDATE/VIEW/CTASなどで列名が重複したと判断されると失敗する。典型メッセージ、発生条件、切り分けSQL、確実に直すための具体的修正例(IF NOT EXISTS/別名付与/DDLの冪等化/ORM設定見直し)をまとめた。再発防止チェックリストと再現~解消の通し手順付き。
- 1. エラーの意味と代表メッセージ
- 2. 発生条件の早見表
- 3. まず確認:情報スキーマで対象テーブルの列を洗い出す
- 4. CREATE/ALTER TABLEでの重複:正しい修正パターン
- 5. INSERT/UPDATE/COPYの列リスト重複
- 6. ビュー/CTAS/SELECT INTOでの重複(SELECTの結果列名がぶつかる)
- 7. SELECT * と JOINの落とし穴(ONとUSINGの違い)
- 8. ALTER TABLE RENAMEで既存列と衝突
- 9. 大小文字・引用符の落とし穴(”UserID” と userid)
- 10. マイグレーション/ORMの二重実行対策(冪等にする)
- 11. ジョイン対象の「衝突候補列名」を事前に洗い出すSQL
- 12. 安全に「列を統合/置換」する手順(実データありのケース)
- 13. 再発防止チェックリスト
- 14. 再現→解消の通し例(CTASでの重複)
エラーの意味と代表メッセージ
・テーブルやビューの「列名」は一意である必要がある
・代表的なメッセージ
ERROR: column "a" specified more than once
ERROR: column "a" of relation "t" already exists
ERROR: column name specified more than once発生条件の早見表
・CREATE TABLE定義内で同一名の列を二重に定義
・ALTER TABLE … ADD COLUMN が既存列に衝突
・INSERT/UPDATE/COPYで列リストに同じ列名を重複指定
・CREATE VIEW / MATERIALIZED VIEW / CREATE TABLE AS / SELECT INTO の出力列名が重複
・SELECT * と JOIN(特にON句)で同名列が複数出て、そのままテーブル/ビュー化
・ALTER TABLE … RENAME COLUMN の新名称が既存列と衝突
・ORM/マイグレーションで同じDDLが再実行され、既存列とぶつかる
まず確認:情報スキーマで対象テーブルの列を洗い出す
-- テーブルの現行列一覧(大小文字・クォートは注意)
SELECT column_name, data_type
FROM information_schema.columns
WHERE table_schema = 'public' AND table_name = 't'
ORDER BY ordinal_position;CREATE/ALTER TABLEでの重複:正しい修正パターン
・NG(CREATE TABLEで同名列を2回)
CREATE TABLE t (
id bigint,
id text -- ERROR: column "id" specified more than once
);・OK(列名を変える/設計を見直す)
CREATE TABLE t (
id bigint,
id_text text
);・NG(既存列にADD COLUMN)
ALTER TABLE t ADD COLUMN id bigint;
-- ERROR: column "id" of relation "t" already exists・OK(冪等化)
ALTER TABLE t ADD COLUMN IF NOT EXISTS id bigint;INSERT/UPDATE/COPYの列リスト重複
・NG(同じ列を2回指定)
INSERT INTO t (id, id, name) VALUES (1, 2, 'x');
-- ERROR: column "id" specified more than once・OK(1回だけ指定)
INSERT INTO t (id, name) VALUES (1, 'x');・UPDATEでも同様
UPDATE t SET id = 1, id = 2;
-- ERROR: column "id" specified more than once・COPYでも列リストの重複は不可
ビュー/CTAS/SELECT INTOでの重複(SELECTの結果列名がぶつかる)
・NG(同じ別名)
CREATE VIEW v AS
SELECT 1 AS a, 2 AS a;
-- ERROR: column "a" specified more than once・OK(別名を分ける)
CREATE VIEW v AS
SELECT 1 AS a1, 2 AS a2;・CTAS/SELECT INTOでも同様
SELECT a.id AS id, b.id AS id INTO t2 FROM a JOIN b ON a.id=b.id;
-- ERROR(重複)SELECT * と JOINの落とし穴(ONとUSINGの違い)
・ON句でJOIN + SELECT * は、両テーブルの同名列が「2本」出る
→ それをCREATE VIEW/CTAS/SELECT INTOに流すと重複で失敗
-- 回避:明示的に別名を振る
CREATE VIEW v AS
SELECT a.id AS a_id, b.id AS b_id, a.*, b.*
FROM a JOIN b ON a.id = b.id;・USING/NATURALは同名列を1本に「畳み込む」挙動(SELECT *の結果では重複しない)
→ ただし読みやすさ優先で明示別名にする方が安全
ALTER TABLE RENAMEで既存列と衝突
・NG
ALTER TABLE t RENAME COLUMN name TO id;
-- ERROR: column "id" of relation "t" already exists・OK
ALTER TABLE t RENAME COLUMN name TO full_name;大小文字・引用符の落とし穴(”UserID” と userid)
・未引用識別子は小文字化される(UserID → userid)
・引用符で囲んだ列名は大小文字を保持する(”UserID” は別物)
・プロジェクト全体で「基本は未引用・小文字」の方針に揃えると衝突/混乱を避けやすい
マイグレーション/ORMの二重実行対策(冪等にする)
・ADD COLUMN/ADD CONSTRAINT は IF NOT EXISTS を使える箇所は極力利用
・スキーマバージョンテーブル(schema_migrations等)で適用済みを確実に記録
・手動実行と自動マイグレーションの二重適用を禁止
ジョイン対象の「衝突候補列名」を事前に洗い出すSQL
-- 2テーブル間で同名の列を列挙(public.a と public.b の例)
SELECT a.attname AS dup_column
FROM pg_attribute a
JOIN pg_attribute b
ON a.attname = b.attname
WHERE a.attrelid = 'public.a'::regclass
AND b.attrelid = 'public.b'::regclass
AND a.attnum > 0 AND b.attnum > 0
ORDER BY a.attname;・出力に出てきた列は別名を割り当てる方針にする
安全に「列を統合/置換」する手順(実データありのケース)
-- 1) 新しい列を作成
ALTER TABLE t ADD COLUMN id_new bigint;
-- 2) データ移行
UPDATE t SET id_new = id;
-- 3) 参照側(ビュー/コード)を id_new に切替
-- 4) 古い列を削除 or 退避名にリネーム(メンテ時間に合わせて)
ALTER TABLE t DROP COLUMN id;
ALTER TABLE t RENAME COLUMN id_new TO id;再発防止チェックリスト
・DDLはIF NOT EXISTS/IF EXISTSで冪等化
・SELECT * + JOIN をそのままテーブル/ビュー化しない(必ず別名設計)
・リネーム時は既存列との衝突を情報スキーマで事前チェック
・ORMの自動DDLは差分の正当性をレビュー
・識別子ポリシー(小文字・未引用)を統一
再現→解消の通し例(CTASでの重複)
-- 準備
DROP TABLE IF EXISTS a, b;
CREATE TABLE a(id int, name text);
CREATE TABLE b(id int, note text);
INSERT INTO a VALUES (1,'alice');
INSERT INTO b VALUES (1,'memo');
-- NG:SELECT * + JOIN + CTAS(id が2本出る)
CREATE TABLE ab AS
SELECT * FROM a JOIN b ON a.id = b.id;
-- ERROR: column "id" specified more than once
-- 解消:出力列に別名を付けてCTAS
CREATE TABLE ab AS
SELECT a.id AS a_id, b.id AS b_id, a.name, b.note
FROM a JOIN b ON a.id = b.id;
-- 確認
SELECT * FROM ab;-
前の記事
PostgreSQL『function sequence error(HY010/S1010)』の原因と対処【psqlODBC/ODBC経由】 2025.09.02
-
次の記事
PostgreSQL「column reference is ambiguous」の原因と対処 2025.09.03
コメントを書く