我有以下三种模型:
class Parent < ApplicationRecord
has_many :children
has_many :assets
end
class Child < ApplicationRecord
belongs_to :parent
end
class Asset < ApplicationRecord
belongs_to :parent
end
现在,我需要通过父母找出属于孩子的资产。并且“资产”具有asset_type列。所以我需要做这样的事情
Parent.first.children.each do |child|
child.parent.assets.find_by(asset_type: "first").asset_value
end
如何避免N + 1个查询?
导轨:5.1.6
红宝石:2.3.4
答案 0 :(得分:3)
第一个问题是,无论您已预加载了什么,添加find_by
都会始终执行另一个查询(至少从Rails 4开始,我怀疑它是否已更改)。这是因为实现了find_by
以生成更多的SQL。如果要预加载,则可以使用find
代替,只要每个父级的资产数量都不是可笑的,这很好,但是如果有很多资产和/或它们很大,则不好会占用大量内存的对象(有关替代解决方案,请参见下面的注释)。
您可以这样预加载资产:
parent.children.preload(:parent => :assets) each do |child|
# Will not execute another query
child.parent.assets.find{ |asset| asset.asset_type == "first" }
或者,您可以声明一个has_many :through
关联:
class Child < ActiveRecord::Base
belongs_to :parent
has_many :assets, through: :parent
...
end
然后您可以简单地
parent.children.preload(:assets).each do |child|
# Will not execute another query
child.assets.find { |asset| asset.asset_type == "first" }
如果要在db层而不是ruby中执行查找,则可以定义一个范围关联:
class Parent < ActiveRecord::Base
has_one :first_asset, ->{ where asset_type: "first" }
...
end
这样,您可以preload(:parent => :first_asset)
代替。