问题
我有以下ActiveRecord模型:
class Person belongs_to :favourite_car,class_name: 'Car' belongs_to :business_car,class_name: 'Car' belongs_to :home_car,class_name: 'Car' end
当我想访问所有这三个关联时,它会生成三个选择查询:
SELECT * FROM cars WHERE cars.id = ?
这基本上是N 1问题.
理想情况下,我希望它只生成一个表单查询
SELECT * FROM cars WHERE cars.id IN (?,?,?)
可能的方案
我可以把它移到has_many:through => :join_table与连接表中的列关联以指示关联类型是什么,然后使用includes([:join_table,:cars])来急切加载关联.但是,在这种情况下,它只将3个查询减少到2,并引入了一个额外的表.
另一种可能的方法
另一种可能的解决方案是手动加载关联,如下所示:
module EagerLoader def eager_load(*associations) reflections = associations.map { |association| self.class.reflections[association.to_sym] } raise 'Not all are valid associations' if reflections.any?(&:nil?) reflections.group_by { |association| association.klass }.each do |klass,reflections| load_associations(klass,reflections) end self end private def load_associations(klass,reflections) primary_key = klass.primary_key ids = reflections.map { |reflection| public_send(reflection.foreign_key) } records = klass.where(id: ids) reflections.each_with_index do |reflection,i| record = records.find do |record| record.public_send(primary_key) == ids[i] end public_send("#{reflection.name}=",record) end end end
我测试了它,它的工作原理.
class Person include EagerLoader end Person.find(2).eager_load(:favorite_car,:business_car,:home_car)
但是,当你想要做的事情时,这仍然无法帮助你
Person.includes(:favourite_car,:home_car)
例如,在人员索引页面上.这会将查询数量从3N减少到4,但实际上只需要2个.
有没有更好的解决方案来解决这个问题?
解决方法
关于手动Eager加载有一篇很棒的帖子.
我想这就是你一直在寻找的东西:
owners = People.all association_name = :photos owners.each do |owner| record = whatever_you_want association = owner.association(association_name) association.target = record association.set_inverse_instance(record) end
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。