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

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

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

症状が起きる例

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

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

idnameemailorganization_idweapon_id
1Alicealice@example.com11
2Bobbob@example.com22
3Charliecharlie@example.com33

organizations テーブル (組織情報)

idcodeorganization_name
1A001Alpha
2B002Bravo
3C003Charlie

weapons テーブル (武器情報)

idcodeweapon_name
1W001Sword
2W002Gun
3W003Bow

通常の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;

出力は次の通りです。

idnameemailorganizations.codeweapons.code
1Alicealice@example.comA001W001
2Bobbob@example.comB002W002
3Charliecharlie@example.comC003W003

organizations.code と weapons.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();

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

idnameemailcode
1Alicealice@example.comW001
2Bobbob@example.comW002
3Charliecharlie@example.comW003

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

解決方法

同名のカラムがある場合、エイリアスを使ってカラム名を変更する必要があります。以下のクエリでは、organizations.code を organization_codeweapons.code を weapon_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();

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

idnameemailorganization_codeweapon_code
1Alicealice@example.comA001W001
2Bobbob@example.comB002W002
3Charliecharlie@example.comC003W003

まとめ

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

コメント

タイトルとURLをコピーしました