そもそもSQLの勉強不足だということもあり躓いたので、メモします。
group byとorder byをあわせて使いたい
以下のようなmessage_searchesテーブルがあるとします。
id | message_room_id | message_id |
---|---|---|
1 | 10 | 2 |
2 | 30 | 4 |
3 | 10 | 1 |
4 | 10 | 5 |
5 | 20 | 6 |
6 | 20 | 3 |
このテーブルにおいて、各message_room_idにおいて、message_idが一番大きいレコードのみを取得したいと思います。手順としては、message_idでorder byをしてからmessage_room_idでgroup byをします。
欲しい結果は、idが2, 4, 5のレコードです。
group byされてからorder byされてしまう
MessageSearch.order(message_id: :desc).group('message_room_id')
# => SELECT `message_searches`.* FROM `message_searches` GROUP BY `message_searches`.`message_room_id` ORDER BY `message_searches`.`message_id` DESC
上記SQLから、order byを先に記述しても、先にgroup byが実行されてしまいます。(RailsというよりSQLの基本)
結果、GROUP BYにより、idが1, 2, 5のレコードが取得され、ORDER BYにより、idが5, 2, 1の順番に並べ替えられます。
解決方法
以下リンクを参考に、fromを使うことでサブクエリが使えます。(サブクエリの中の処理が先に実行されます。)
http://api.rubyonrails.org/v4.2.10/classes/ActiveRecord/QueryMethods.html#method-i-from
.from(サブクエリ内の処理, :エイリアス名)
MessageSearch.group('message_room_id').from(MessageSearch.order(message_id: :desc), :message_searches)
# => SELECT `message_searches`.* FROM (SELECT `message_searches`.* FROM `message_searches` ORDER BY `message_searches`.`message_id` DESC) message_searches GROUP BY message_room_id
先にORDER BYされてからGROUP BYされるようになりました。
2019/6/24に追記
最初はこのやり方で検索をかけていましたが、10万件を超えるレコードだと圧倒的にパフォーマンスが落ちてしまいました。(今更感で申し訳ございませんm(_ _)m)したがって、サブクエリを使ったやり方はあまりおすすめしません。
どのような対処をしたかというと、今回のケースだと、以下lastest_messagesテーブルを作成し、こちらのテーブルで検索するようにしました。message_searchesテーブルでレコードが作られた際、after_saveでlastest_messagesテーブルの関係するlastest_message_idを更新するようにしています。
id | message_room_id | lastest_message_id |
---|---|---|
1 | 10 | 5 |
2 | 30 | 4 |
3 | 20 | 6 |