ActiveRecord两级关联查询问题:Factory关联Worker查询报错及优化咨询
问题场景与需求
模型关联关系
Factory、Worker、JobRole三者为has_many :through关联,具体模型代码如下:
Factory 模型
has_many :workers, through: :job_roles
Worker 模型
has_many :job_roles has_many :factories, through: :job_roles
JobRole 模型
belongs_to :worker belongs_to :factory
尝试的查询与问题
我尝试执行以下查询语句:
factory.job_roles.where(heavy_lifting:true, sensitive_area: false).workers.where.not(confirmed_at:nil).pluck(:work_capacity)
但factory.job_roles.where(...)返回的是ActiveRelation实例,直接调用.workers方法会报错。
需求
获取某工厂内满足以下条件的Worker的work_capacity字段值:
- 关联的JobRole满足
heavy_lifting: true且sensitive_area: false - Worker已确认(
confirmed_at不为空)
请问除了循环遍历的方式(如下示例代码),是否有更高效的实现方法?
循环遍历示例代码
applicable_job_roles = factory.job_roles.where(heavy_lifting:true, sensitive_area: false) work_capacities = [] applicable_job_roles.each do |job_role| if job_role.worker.confirmed_at != nil work_capacities << job_role.worker.work_capacity end end
注:原代码中job_role.confirmed_at应为job_role.worker.confirmed_at,confirmed_at是Worker模型的字段
解决方案
可以通过ActiveRecord构造单条SQL查询语句,避免N+1查询和内存循环处理,以下是几种可行写法:
方法1:从Worker模型直接查询
直接从Worker模型入手,通过关联筛选工厂和JobRole的条件:
Worker.joins(:job_roles) .where(job_roles: { factory_id: factory.id, heavy_lifting: true, sensitive_area: false }) .where.not(confirmed_at: nil) .pluck(:work_capacity)
方法2:基于Factory实例的关联查询
利用Factory的workers关联,结合joins和merge合并JobRole的筛选条件:
factory.workers .joins(:job_roles) .merge(JobRole.where(heavy_lifting: true, sensitive_area: false)) .where.not(confirmed_at: nil) .pluck(:work_capacity)
这种写法更贴合从Factory实例出发的查询逻辑,merge可以将JobRole的查询条件无缝整合到关联查询中。
方法3:预加载关联后内存筛选(备选)
如果需要同时获取Worker对象,可通过includes预加载关联避免N+1,再在内存中筛选,但性能不如前两种直接生成SQL的方式:
factory.job_roles .where(heavy_lifting: true, sensitive_area: false) .includes(:worker) .select { |jr| jr.worker.confirmed_at.present? } .map { |jr| jr.worker.work_capacity }
内容的提问来源于stack exchange,提问作者daveasdf_2
相关产品推荐
相关产品推荐

