やりたいこと
・グループごとの集計人数を出したい
・SQL
はかけるがLaravel
のクエリビルダ
の使い方がわからない
Laravelでなかなかうまくいかなかったので学びを記事にしました。
テーブル構造
例えば以下のようなテーブルがある時
↓(親)groupsテーブル
# | name |
---|---|
1 | group1 |
2 | group2 |
3 | group3 |
4 | group4 |
5 | group5 |
6 | group6 |
↓(子)usersテーブル
# | name | group_id |
---|---|---|
1 | user1 | 1 |
2 | user2 | 1 |
3 | user3 | 2 |
4 | user4 | 2 |
5 | user5 | 3 |
6 | user6 | 4 |
SQL文
「group」ごとに何人の「user」が所属しているかを集計したいとき。
素のSQL文では以下のようになります。
select
g.id, g.name, count(u.group_id)
from
groups g
left outer join users u
on g.id = u.group_id
group by u.group_id;
#クエリビルダ
Laravel
で表すと以下になります。
$groups = DB::table('groups') //$groupsは変数にいれてるだけ
->leftJoin('users', 'groups.id', '=', 'users.group_id')
->select('groups.id', 'groups.name', DB::raw("count(users.group_id) as count"))
->groupBy('groups.id')
->get();
・外部結合は leftjoin()
(内部結合してしまうと誰も所属してないグループが無視される)
・グループ化はgroupBy()
・集計関数は素のSQL文で書く必要あるためDB::raw()
を使います
(私は集計関数に時間かかりました)
groupByがうまく動かない問題
私の場合は以下のようなエラーが出ました。
SQLSTATE[42000]: Syntax error or access violation: 1055 '(アプリ名.groups.name' isn't in GROUP BY
これが書き方なのか何のエラーかわからず解決に時間かかってしましました。
実は Laravel
のバージョンによってはうまく動作しません。
ちなみに私はLaravel8
です。
“strict”モードをfalseは本質的な解決ではない
コメントでご指定をいただき訂正します。
以前は下記のようなやり方で解決したと思っておりました。
“strict”モードをfalseにし本質的な解決ではない以前のやり方
結論いうと
config/database.phpの “strict”モードをfalseにすることでエラーが表示されなくなります。
'mysql' => [
'driver' => 'mysql',
'url' => env('DATABASE_URL'),
'host' => env('DB_HOST', '127.0.0.1'),
'port' => env('DB_PORT', '3306'),
'database' => env('DB_DATABASE', 'laravel-blog'),
'username' => env('DB_USERNAME', 'root'),
'password' => env('DB_PASSWORD', ''),
'unix_socket' => env('DB_SOCKET', ''),
'charset' => 'utf8',
'collation' => 'utf8_unicode_ci',
'prefix' => '',
'prefix_indexes' => true,
'strict' => false,//←この部分をtrueからfalseへ
'engine' => null,
'options' => extension_loaded('pdo_mysql') ? array_filter([
PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'),
]) : [],
],
これでうまく表示されるようになります!!
MySQL
のセキュリティみたいなものがデフォルトで厳しく?なってるみたいで
グループ化による重複するレコードが問題みたい・・・です(^_^;)
この問題を最初に解決した人スゴイ(*_*)
実際のエラーの内容は
「selectで設定したカラムを全てgroup byにせよ」
ということが再度調べたら判明しました!
このことを私は理解できてませんでした(@@)
ご指摘ありがとうございます!
$groups = DB::table('groups') //$groupsは変数にいれてるだけ
->leftJoin('users', 'groups.id', '=', 'users.group_id')
->select('groups.id', 'groups.name', DB::raw("count(users.group_id) as count"))
->groupBy('groups.id')
->groupBy('groups.name') //←こちらを追加
->get();
これで**“strict”モードをfalseにすることなく**正常に動作しました^^
結果
先ほどの$group
をforeach
で取り出すと
groups.id | groups.name | count |
---|---|---|
1 | group1 | 2 |
2 | group2 | 2 |
3 | group3 | 1 |
4 | group4 | 1 |
5 | group5 | 0 |
6 | group6 | 0 |
こんな感じになります (^-^)
これでビューへ渡す変数が一つで済むし表示が簡単になります!