🔥

# 【SQL】SELECT * と INNER JOIN で起きる duplicate column name エラーの解決法

に公開

はじめに

SQLのJOIN操作で「手軽に全カラム取得したい」と思ってSELECT *を使ったら、予期しないエラーに遭遇したことはありませんか?

今回はSELECT *INNER JOINを組み合わせる際のUSING句とON句の使い分けについて、実際に発生し得るエラーと解決策を解説します。

🚨 問題のあるSQL

以下のSQLは僕が書いた

-- ❌ 重複列が発生しやすいパターン(ON + SELECT *)
SELECT *
FROM users u
INNER JOIN orders o ON u.user_id = o.user_id;

一見問題なさそうに見えますが、実際には同名カラムが結果セットに並ぶため、これをビュー化したり、ORM/BIツールに流したりすると:

Error: duplicate column name 'user_id'

といったエラーの原因になります(DBやツールによっては、SELECT単体は通ってもスキーマ化で落ちます)。

✅ 解決策

1. USING句を使用する(対応DBなら推奨)

-- ✅ SELECT * を使いたいなら USING が安全(MySQL / PostgreSQL / SQLite)
SELECT *
FROM users u
INNER JOIN orders o USING (user_id);

ポイント

  • 結合キーが1列に統合されるため、SELECT *でも重複列名にならない
  • 記述が簡潔で読みやすい

2. カラムを明示的に指定(最推奨)

-- ✅ 必要なカラムのみ明示(DB/ツールに最も優しい)
SELECT 
  u.user_id,
  u.name,
  u.email,
  o.order_id,
  o.amount
FROM users u
INNER JOIN orders o ON u.user_id = o.user_id;

ポイント

  • 転送量削減・スキーマ変化耐性・可観測性の観点でもベスト
  • ビュー化やAPI公開を見据えた"壊れにくい"SELECT

3. テーブル指定でSELECT * + 別名付け

-- ✅ table.* と必要列だけを組み合わせ、衝突する列は別名にする
SELECT 
  u.*,
  o.order_id,
  o.amount,
  o.user_id AS order_user_id  -- ← 衝突するなら別名で回避
FROM users u
INNER JOIN orders o ON u.user_id = o.user_id;

ポイント

  • どうしても*を使いたい場合の妥協案
  • ビュー化や外部公開前に"列名一意"を必ず担保

🔍 エラーの原因

USING句の特性

USING句は結合条件を簡潔に書けて便利で、以下の特性があります:

  • 両テーブルの結合カラムを自動的に1列へ"統合"
  • テーブル名(エイリアス)の指定が不要
  • 結果セットには統合されたカラムが1つだけ含まれる

SELECT * と ON句の組み合わせ問題

-- テーブル構造例
users:   user_id, name, email
orders:  order_id, user_id, amount

-- ON句 + SELECT * の実行時
SELECT *
FROM users u
INNER JOIN orders o ON u.user_id = o.user_id;

この場合、SQLエンジンは以下の処理を行います:

  1. u.user_ido.user_idを比較して結合
  2. SELECT *で全カラムを取得
  3. u.user_ido.user_idの"同名カラムが両方とも残る"
  4. 結果セット自体は許容されても、ビュー作成や一部ツールが"duplicate column name"でエラー

対してUSING (user_id)を用いると結合キーは1列に統合され、SELECT *でも同名カラムの衝突が起きません(※SQL ServerはUSING非対応)。

📚 詳細解説

USING vs ON の使い分け

項目 USING句 ON句
記述の簡潔性
SELECT * との相性 ×
複雑な条件
可読性 ◎(結合キーが明確) ◎(柔軟だが冗長になりがち)
重複カラム回避 ◎(統合) ×(両方残る)

補足:SQL Server は USING 非対応のため、常に ON を使います。その際は列の明示・別名付けが実務上の必須テクニックです。

💡 覚えておくべきポイント

黄金ルール

SELECT * を使うなら USING を優先(対応DBのみ)。
ON を使うときは"必要な列だけ明示"し、衝突列には必ず別名を付ける。
ビュー化・外部公開前に"列名の一意性"を必ず確認。

なぜこの方針が安全なのか

  1. 列名一意性: ツール連携・ビュー作成でコケない
  2. 拡張性: 複雑条件は ON、それ以外は USING でシンプルに
  3. 可読性/保守性: 明示的な列リストはレビューと差分追跡に強い
  4. パフォーマンス: 過剰データ転送を防ぎ、計測も容易

まとめ

  • USING句は結合キーを統合するため、SELECT *との相性が良い(MySQL / PostgreSQL / SQLite)
  • ON句は同名カラムが両方残るため、SELECT *だと duplicate column name の原因になりやすい(特にビュー作成やツール連携時)
  • 最も確実なのは"列を明示"し、衝突し得る列には別名を付けること
  • SQL Server は USING が使えないため、明示列 + 別名付けが必須の作法

SQLの結合は基本機能ですが、列名の一意性を意識しておくだけで、運用・連携時のトラブルを大幅に減らせます。エラーに遭遇したら、まずJOIN方法と列名を見直しましょう。


Discussion