Laravelを使ったアプリケーションで、テーブル結合を行う際に select
で指定したカラムが正しく出力されないことがあったので軽く調べてみると、Laravelのクエリビルダを使う場合、結合時に同名のカラムがあると上書きされることが分かりました。
当時この問題にぶつかった際はエラーも発生せず、出力も行われるため気づくのが遅れたのですが、再発防止のため、メモとして書き残します。
症状が起きる例
以下のテーブルを結合します。出力したい項目は users.id
、users.name
、users.email
、organizations.code
、weapons.code
。
users テーブル (ユーザー情報)
id | name | 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 | 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.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();
出力結果は以下のようになります。
id | name | code | |
---|---|---|---|
1 | Alice | alice@example.com | W001 |
2 | Bob | bob@example.com | W002 |
3 | Charlie | charlie@example.com | W003 |
最後に指定した weapons.code
が organizations.code
を上書きしてしまっています。
解決方法
同名のカラムがある場合、エイリアスを使ってカラム名を変更する必要があります。以下のクエリでは、organizations.code
を organization_code
、weapons.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();
結果は次のようになり、上書きされることがなくなります。
id | name | 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で同名のカラムを扱う際には、エイリアスを使用してカラム名を明確にすることを推奨。
- そもそも曖昧さを防ぐのと可読性の観点からも異なるテーブルに同じカラム名を使うのは避けるべき。