Laravelクエリビルダーで複数の集計関数をメソッドチェーンで使おうと思ったら詰まった。
結論からいうとメソッドチェーンで書くのはムリ! Laravel4.2で試したけどたぶん5.x系もムリだと思われる。

目次

  1. クエリビルダー
  2. MySQLの集計関数

クエリビルダー

Laravelクエリビルダーってのは前までは「fluent」っていう名前だったらしいが、4.2か5.0から名前がなくなったらしい。
ちなみに「fluent」は流暢って意味らしい。カッコいいね~

で、件のメソッドチェーンはこんな感じ。
サンプルは適当なのでテーブルも適当です。ゴメンナサイ。

1
2
3
4
DB::table('divisions')
->select('division_id','division_name')
->max('updated_at')
->groupBy('division_id','division_name');

どうも集計関数は 1つのSQLに1つしか書けない らしい。
従って、以下のようなのもダメ。

1
2
3
DB::table('scores')
->max('score')
->min('score');

なんでこんなこともできね~んだ! という怒りは置いておいて、できないのは何かしらの理由があるんだろう。
できないものはできない。仕方ないので別のやり方を探す。

結局、ここを参考にした。
最初のSQLをクエリビルダーで書くとこうなる。

1
2
3
DB::table('scores')
->select(['division_id','division_name', DB::raw('MAX(updated_at)'])
->groupBy('division_id','division_name');

select内に各カラムを配列で記述し、集計関数を使うところはDB::rawに直接書く。
ちなみにSELECT句にCASE式を書いたりする場合も同様の方法で行う。

集計関数を続けて複数書きたい場合は次のように書く。

1
2
DB::table('scores')
->select([DB::raw('MAX(score), MIN(score)']);

以上。
なお、上記のクエリ群は実際に実行してないので間違ってるとこがあるかも…

どうでもいいけど、クエリビルダーを使った場合はサブクエリもうまく書けなかった。正確には書けたものの実行したら思った値が取れない。(ログ見ると正しいSQLが実行されているので別の問題?)
サブクエリの質問はStackOverflowとかにころころ転がってる。みんな困ってんだね。

SQL全部直接書かせてほしいな~という気持ちを抑えながら使ってる。

MySQLの集計関数

で、クエリビルダーとは全然関係ない話になるが、これをやってて気づいたのがMySQLは集計関数を通しているカラムと通していないカラムが混在しても実行できてしまう。

最初は「え?実行できんの?サブクエリ書かなくてラッキー」 と思ったものの 「値おかしくならないか?」 と取得結果を見たらやはりおかしかった…
Twitteでもご指摘頂いたけどSQLアンチパターンの「曖昧なグループ」ってやつらしい。

これが許されているのはMySQLとSQLiteだけらしい…こんなのこの2つ以外使ったことない人とか気づかないだろ…(もっとも取得結果でわかるけど…)

詳しい説明は下記のリンクで

14章 アンギュアスグループ(曖昧なグループ)
MySQLのGROUP BYは、寛容すぎて気持ちが悪い。
MySQLの拡張仕様、GROUP BYでカラム指定を省略してSELECTする場合について