如何减少Rails ActiveRecord关联对象的查询次数
方案1:通过关联预加载解决N+1查询
先在AuthorizedUser中定义基于邮箱的关联(前提是User的email字段有唯一约束,保证关联的唯一性):
class AuthorizedUser < ApplicationRecord belongs_to :user, primary_key: :email, foreign_key: :email, optional: true end
接着在Organization的方法里预加载这个关联,一次性拉取所有关联用户数据,彻底避免N+1查询:
class Organization < ApplicationRecord has_many :authorized_users def authorized_user_opts self.authorized_users.includes(:user).map do |au| [au.email, au.user&.id] # 用&.避免用户不存在时抛出异常 end end end
方案2:直接用JOIN查询一次性获取目标数据
如果不想修改关联关系,也可以直接通过SQL JOIN一次性查询所需字段,性能更优:
class Organization < ApplicationRecord has_many :authorized_users def authorized_user_opts self.authorized_users .joins("JOIN users ON users.email = authorized_users.email") .pluck("authorized_users.email", "users.id") end end
pluck会直接返回你需要的二维数组格式,全程仅发起一次SQL查询。
补充优化建议
- 务必给
User的email字段添加唯一索引,既保证关联查询的效率,也避免一个邮箱对应多个用户的混乱:class AddUniqueIndexToUsersEmail < ActiveRecord::Migration[6.1] def change add_index :users, :email, unique: true end end - 如果存在
AuthorizedUser无对应User的场景,方案2的joins会自动过滤这类记录。若要保留它们,改用left_joins即可:
此时无对应用户的条目会返回self.authorized_users .left_joins("LEFT JOIN users ON users.email = authorized_users.email") .pluck("authorized_users.email", "users.id")[邮箱地址, nil]。
内容的提问来源于stack exchange,提问作者Billbryson78
相关产品推荐
相关产品推荐

