こんにちわ、東京は三寒四温ならぬ七寒三温くらいの日々に辟易しているHashiです。今回はLaravelでグループ化したデータの中から各グループの最新レコードのみを取得したいケースに遭遇して、少し手こずったので備忘録も兼ねて紹介したいと思います。
なお、Laravelとはなんぞやという方は下の記事を参照してください!
環境
今回の環境は下記の通りとなります。
今回の環境
- Laravel 5.5
- MySQL 5.6.37
仕様と要点
まずは、今回の仕様として下記のようなテーブルがあると仮定します。ちなみに、テーブル名は「status_histories」とします。
ID | user_id | status |
---|---|---|
1 | 1 | 未申請 |
2 | 1 | 申請中 |
3 | 1 | 承認 |
4 | 2 | 未申請 |
5 | 2 | 申請中 |
6 | 3 | 未申請 |
このテーブルには上の表のように「id」,「user_id」,「status」という3つのカラムから構成されているテーブルで、見ての通りユーザーのステータス履歴となっています。
求める結果
そして、今回このテーブルから欲しい情報は、それぞれのユーザーの最新レーコドということになるので、求める結果は下記の通りとなります。
ID | user_id | status |
---|---|---|
3 | 1 | 承認 |
5 | 2 | 申請中 |
6 | 3 | 未申請 |
グループ化したデータから最新レコードを取得する
「GROUP BY」と「ORDER BY」を組み合わせてみる
さて、各グループの最新レコードは取得していきたいと思いますが、冒頭のとおり少し手こずりました。最初に試したのは下記のコード。
StatusHistory::groupBy('user_id')->orderBy('id','desc')->get();
見ての通り、「GROUP BY」句と「ORDER BY」句を組み合わせた方法です。グループ化してそれぞれの最新レコードが欲しければ、まず単純にグループ化してソートすればいいと思いますよね。でも、この方法だと期待通りの結果は取得できないんですね。うまくいかない理由は処理の順番にあり、「GROUP BY」と「ORDER BY」の組み合わせの場合は、まず「GROUOP BY」が先に処理されるので、ユーザーIDでグループ化された後にソートされたデータが返ってきてしまうからです。
つまり、上記の方法だと下表のような結果になるということになります。
ID | user_id | status |
---|---|---|
6 | 3 | 未申請 |
4 | 2 | 未申請 |
1 | 1 | 未申請 |
これでは、全然求めるものと違いますね。
「MAX」を使ってみる
「GROUP BY」と「ORDER BY」の組み合わせではダメということがわかったので、次に試したのが「MAX」を使う方法です。コードはこちらになります。
StatusHistory::select(DB::raw('MAX(id) As id, user_id, status'))->groupBy('user_id')->get();
この場合、結果がどうなるかというと下表のようになります。
ID | user_id | status |
---|---|---|
3 | 1 | 未申請 |
5 | 2 | 未申請 |
6 | 3 | 未申請 |
「id」に関しては期待通りのidが返ってきますが、「user_id」と「status」に関しては、最新レコードとは違うレコードのものを取得してしまいます。ということでこれもダメですね。
サブクエリを使ってみる
そして、最後に試したのがサブクエリを使う方法です。上の「MAX」を使う方法だとidに関しては期待通りの結果が得られるので、上記の方法をサブクエリとして使用してみます。今回は下記のように「whereIn」と組み合わせてみました。
StatusHistory::whereIn('id', function($query) {
$query->select(DB::raw('MAX(id) As id'))->from('status_histories')->groupBy('user_id');
})->get();
サブクエリで取得したid(各グループの最新レコード)を、whereInを使って取得しています。これでようやく期待通りの結果が得られました。めでたし、めでたし。しかし、もっとスマートな方法はないものか。。
まとめ
ということで、今回はLaravelでグループ化したデータの中から各グループの最新レコードを取得する方法を紹介しました。もっとスマート方法がありましたら教えてください。
それでは、今回はここまで!Hashiでした。また!
この記事のまとめ
- Laravelでグループ化したデータの中から各グループの最新レコードを取得する方法