如何获取归属关联记录?Rails中高效获取关联对象Relation方案咨询
嘿,这个问题我太有发言权了!你现在用的collect方法确实踩了两个坑:一是触发N+1查询(先查所有Post,再挨个查每个Post对应的User,数据量大时效率极低);二是返回普通数组,没法继续用ActiveRecord的链式查询能力(比如加筛选、排序)。下面给你几个更优的实现方案,按需选择:
方案1:预加载关联,高效获取关联对象数组
如果你的核心需求是拿到所有Post对应的User对象(允许重复,比如同一个User有多篇Post就会多次出现),同时解决N+1问题,用includes预加载关联是最优解:
# 先预加载所有Post关联的User,只会触发2次SQL查询 posts_with_users = Post.includes(:user) # 提取User对象,返回数组但查询效率大幅提升 @users = posts_with_users.map(&:user)
注:如果你的关联名称是author而不是user,记得把:user替换成:author
这个方法虽然还是返回数组,但查询次数从N+1降到了2次,性能提升非常明显,而且能保留和Post一一对应的关系(包括Post没有User时返回nil)。
方案2:获取User的ActiveRecord Relation对象
如果你需要返回ActiveRecord::Relation(方便后续链式调用where、order等方法),可以从两个角度实现:
从User模型出发(左连接对应你的SQL)
完全对应你写的LEFT JOIN逻辑,同时返回User的Relation:
# 左连接Post,返回所有关联了Post的User(有多少Post就返回多少次,会重复) @users = User.left_joins(:posts).where.not(posts: { id: nil }) # 如果需要去重,得到所有至少有一篇Post的User,加上distinct @users = User.left_joins(:posts).where.not(posts: { id: nil }).distinct
从Post模型出发,获取关联User的Relation
更简洁的写法,自动去重:
# 先提取所有Post的user_id,再查询对应的User,返回Relation @users = User.where(id: Post.pluck(:user_id))
这个方法只会触发2次SQL查询,而且返回的是纯净的User Relation,方便后续继续操作。
为什么原来的方法效率低?
你原来的写法Post.all.collect { |post| post.user }会先执行一次SELECT * FROM posts,然后遍历每一个Post对象时,又会执行一次SELECT * FROM users WHERE id = ?,总共触发1+N次查询(N是Post的数量)。当Post数量多的时候,数据库会被大量重复请求压垮,而预加载或关联查询的方式能把查询次数控制在2次以内,性能差异巨大。
内容的提问来源于stack exchange,提问作者lisovskey

