Help us understand the problem. What is going on with this article?

ActiveRecordがgroupごとに orderしてくれなくて困った。

More than 1 year has passed since last update.

結論

MySQLの仕様上、処理順はgroup by→order byなので、
Somemodel.order('some desc').group(:some_column) のようにしても
グループごとにソートされた値は取ってこれなかった。

MySQLでGROUP BYとORDER BYを同時に使用する場合に気をつけたいこと
https://norm-nois.com/blog/archives/1293

pluckで配列として取ってきたものを、頑張って処理すればほしい値が取れる。
頑張って配列を処理する方法がわからない方は、
最後まで見ても良いかもしれません。

どんなことがしたかったか

Blogというモデルのblogsテーブルには
id, category, user_id, text_length というカラムを持っていたとして、
user_idのcategoryごとに、text_lengthが最大のidを取得したかった。
(UserとBlogが1対多のイメージです)

最初やろうとしたこと

Blog.order('text_length desc').group(:category,:user_id).pluck(:user_id,:category)
# 出てくると思ってたクエリ
SELECT user_id, category FROM  blogs
ORDER BY text_length desc
GROUP BY category, user_id 

ところがどっこい、出てくるクエリはORDER BY とGROUP BYが逆転してて、
出てくる値も当然期待通りのものではありませんでした。
(生クエリ書いて、逆転させても、エラーが出ます)

GROUP BY が先に実行されるので、
category,user_idごとのgroup(idが一番若いものが代表になる)になってから、
それをtext_lengthでorderするので、全くuserごとのtext_lengthとは関係ないものが出てきてしまいました。

そしてこれができないのはMySQL側の問題のよう(結論の記事をご確認ください)なので、
仕方なくpluckしてRails側でgroup_byしてからorderすることにしました。

以下はただの配列操作したという話

# 本当は全カラム取るならpluckだけでいいですが、
# 順番を明示するためにカラムを明示してpluckしてます
blogs = Blog.pluck(:user_id,:category,:id,:text_length)
#=> blogs = [
# [1, 2, 1, 4], [1, 2, 2, 5], [1, 1, 3, 6], [1, 1, 4, 7], 
# [2, 2, 5, 6], [2, 2, 6, 4], [2, 1, 7, 8], [2, 1, 8, 7]]
grouped_blogs = blogs.group_by {|b| [b.first, b.second]} 
grouped_blogs.each do |_key,value|
  max_ary = value.max_by(&:fourth) # 4つ目がtext_lengthなので. max_byは便利
  p "user_id:#{max_ary[0]} category:#{max_ary[1]} id:#{max_ary[2]} text_length:#{}{max_ary[3]}"
end
#=> # 
#   "user_id:1 category:2 id:2 text_length:5"
#   "user_id:1 category:1 id:4 text_length:7"
#   "user_id:2 category:2 id:5 text_length:6"
#   "user_id:2 category:1 id:7 text_length:8"

こうして欲しい情報を得られました。
pluckした後にカラム名でいい感じにhashにしたいですが、
簡単な方法は調べても出てこなかったので諦めてます。

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away