kamado / LaravelでSQLのテーブル結合で同名カラムが存在する時の書き方

Created Fri, 13 Sep 2024 00:00:00 +0000 Modified Sun, 03 Nov 2024 23:51:25 +0900
1013 Words

Laravelを使ったアプリケーションで、テーブル結合を行う際に select で指定したカラムが正しく出力されないことがあったので軽く調べてみると、Laravelのクエリビルダを使う場合、結合時に同名のカラムがあると上書きされることが分かりました。

当時この問題にぶつかった際はエラーも発生せず、出力も行われるため気づくのが遅れたのですが、再発防止のため、メモとして書き残します。

症状が起きる例

以下のテーブルを結合します。出力したい項目は users.idusers.nameusers.emailorganizations.codeweapons.code

users テーブル (ユーザー情報)

id name email organization_id weapon_id
1 Alice alice@example.com 1 1
2 Bob bob@example.com 2 2
3 Charlie charlie@example.com 3 3

organizations テーブル (組織情報)

id code organization_name
1 A001 Alpha
2 B002 Bravo
3 C003 Charlie

weapons テーブル (武器情報)

id code weapon_name
1 W001 Sword
2 W002 Gun
3 W003 Bow

通常のSQLでは以下のクエリで取得できます。

SELECT 
    users.id, 
    users.name, 
    users.email, 
    organizations.code, 
    weapons.code 
FROM 
    users
JOIN 
    organizations ON users.organization_id = organizations.id
JOIN 
    weapons ON users.weapon_id = weapons.id;

出力は次の通りです。

id name email organizations.code weapons.code
1 Alice alice@example.com A001 W001
2 Bob bob@example.com B002 W002
3 Charlie charlie@example.com C003 W003

organizations.codeweapons.code のカラム名はテーブル名で区別され、上書きはされません。

しかし、Laravelのクエリビルダでは通常のSQLと同じ結果にはなりません。上記のSQLクエリとほぼ同じコードで確認してみます。

$results = DB::table('users')
    ->join('organizations', 'users.organization_id', '=', 'organizations.id')
    ->join('weapons', 'users.weapon_id', '=', 'weapons.id')
    ->select(
        'users.id',
        'users.name',
        'users.email',
        'organizations.code',
        'weapons.code'
    )
    ->get();

出力結果は以下のようになります。

id name email code
1 Alice alice@example.com W001
2 Bob bob@example.com W002
3 Charlie charlie@example.com W003

最後に指定した weapons.codeorganizations.code を上書きしてしまっています。

解決方法

同名のカラムがある場合、エイリアスを使ってカラム名を変更する必要があります。以下のクエリでは、organizations.codeorganization_codeweapons.codeweapon_code として取得します。

$results = DB::table('users')
    ->join('organizations', 'users.organization_id', '=', 'organizations.id')
    ->join('weapons', 'users.weapon_id', '=', 'weapons.id')
    ->select(
        'users.id',
        'users.name',
        'users.email',
        'organizations.code as organization_code',
        'weapons.code as weapon_code'
    )
    ->get();

結果は次のようになり、上書きされることがなくなります。

id name email organization_code weapon_code
1 Alice alice@example.com A001 W001
2 Bob bob@example.com B002 W002
3 Charlie charlie@example.com C003 W003

まとめ

  • 通常のSQLでは、同名のカラムがあっても上書きされることはなく、テーブル名とともに表示されるため、カラムの区別が可能。
  • Laravelのクエリビルダでは、同名のカラムがあると後に結合されたカラムが優先され、前のカラムが上書きされてしまうため、エイリアスを付ける必要がある。
  • SQLやLaravelで同名のカラムを扱う際には、エイリアスを使用してカラム名を明確にすることを推奨。
  • そもそも曖昧さを防ぐのと可読性の観点からも異なるテーブルに同じカラム名を使うのは避けるべき。