こんにちは。
今回は、Railsで使えるTipsとして、特定の条件でデータを取得する時のサブクエリの書き方について紹介します。
こんなケースを想定しています。
- ユーザーが投稿した記事の中で、いいね数が最も多い記事を取得したい
- 記事の中で、最新のコメントのみを取得したい
- 週次で更新されるPV数のスナップショットの中で、最新のレコードのみを取得したい
いいね数が最も多い記事を取得する
いいね数が最も多い記事を取得するには、以下のようにサブクエリを使います。
# app/models/article.rb
class Article < ApplicationRecord
scope :most_liked, -> {
where(id: Like.group(:article_id).order('count(article_id) desc').limit(1).pluck(:article_id))
}
end
このようにすることで、いいね数が最も多い記事を取得することができます。
最新のコメントのみを取得する
最新のコメントのみを取得するには、以下のようにサブクエリを使います。
# app/models/comment.rb
class Comment < ApplicationRecord
scope :latest, -> {
where(id: Comment.group(:article_id).order('max(created_at) desc').pluck(:id))
}
end
# app/models/article.rb
class Article < ApplicationRecord
has_many :comments
has_one :latest_comment, -> { latest }, class_name: 'Comment'
end
このようにすることで、最新のコメントのみを取得することができます。
スナップショットの中で、最新のレコードのみを取得したい
基本的に最新のコメントと一緒と思われるかもしれませんが、こちらはshot_atカラムなどで週次で更新されるPV数のスナップショットの中で、最新のレコードのみを取得する場合です。
# app/models/pv_snapshot.rb
class PvSnapshot < ApplicationRecord
scope :latest, -> {
latest_snapshots = select(
'pv_snapshots.*',
'ROW_NUMBER() OVER (PARTITION BY pv_snapshots.article_id ORDER BY pv_snapshots.shot_at DESC) AS row_number'
).to_sql
from("(#{latest_snapshots}) AS pv_snapshots").where(row_number: 1)
}
end
こちらはrow_numberを使って、各記事ごとに最新のレコードのみを取得しています。
idやcreated_atなどのカラムで確実に最新のものと言い切れる場合はいいですが、後から古いデータを取り込み直すケースが発生する可能性がある場合には、このような方法も使えるかもしれません。
このように、特定の条件でデータを取得する時にサブクエリを使うことで、スッキリとしたコードを書くことができます。
ぜひ参考にしてみてください。