LoginSignup
14
10

More than 3 years have passed since last update.

Ruby on Railsでgroup byとorder byを組み合わせて使う方法

Last updated at Posted at 2018-07-05

そもそも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されてしまう

messages_controller.rb
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(サブクエリ内の処理, :エイリアス名)

messages_controller.rb
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
14
10
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
14
10