結論
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にしたいですが、
簡単な方法は調べても出てこなかったので諦めてます。