Rails如何用单条Active Record查询获取每个feed_id的10条最新Entry记录
实现方案
核心思路
借助数据库窗口函数ROW_NUMBER()实现分组排序取TopN,全程仅生成1条SQL查询,无N+1问题,性能优异,可直接在GraphQL resolver中使用。
代码实现
1. 窗口函数版本(推荐,适配PostgreSQL、MySQL 8.0+)
在Entry模型中新增可复用scope:
class Entry < ApplicationRecord belongs_to :feed # 按feed分组取最新limit条记录,默认取10条 scope :latest_per_feed, ->(limit = 10) do # 给每个feed下的entry按发布时间倒序排名 ranked_entries = select( "entries.*, ROW_NUMBER() OVER (PARTITION BY feed_id ORDER BY published_at DESC) AS feed_entry_rank" ) # 筛选每个feed排名前limit的记录,保持排序规则 from(ranked_entries, :entries) .where("feed_entry_rank <= ?", limit) .order(feed_id: :asc, published_at: :desc) end end
使用方式
# 取所有feed各自最新10条 @entries = Entry.latest_per_feed # 自定义取最新5条 @entries = Entry.latest_per_feed(5) # 关联Feed数据避免N+1查询(适合GraphQL返回feed关联字段场景) @entries = Entry.latest_per_feed.includes(:feed) # 支持链式追加过滤条件,比如仅查询指定feed的最新10条 @entries = Entry.latest_per_feed.where(feed_id: [1,2,3])
2. 低版本MySQL兼容方案(MySQL 8.0以下)
如果使用的MySQL版本不支持窗口函数,可以用相关子查询实现:
scope :latest_per_feed, ->(limit = 10) do where( "SELECT COUNT(*) FROM entries e2 WHERE e2.feed_id = entries.feed_id AND e2.published_at >= entries.published_at <= ?", limit ).order(feed_id: :asc, published_at: :desc) end
验证方法
执行以下代码可确认结果符合预期:
Entry.latest_per_feed.group_by(&:feed_id).each do |feed_id, entries| puts "Feed#{feed_id}返回条目数:#{entries.size}" # 预期输出都是10 end
内容的提问来源于stack exchange,提问作者DK92
相关产品推荐
相关产品推荐

