本文介绍了Rails 5急于加载,然后find_by的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有以下三种模型:

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列。因此,我需要执行以下操作

Now I need to find out the assets that belong to a child through parent. And "Asset" has asset_type column. So I need to do something like this

Parent.first.children.each do |child|
  child.parent.assets.find_by(asset_type: "first").asset_value
end

如何避免N + 1个查询?

How can I do this avoiding N+1 queries?

路轨:5.1.6

红宝石:2.3.4

推荐答案

第一个问题是添加 find_by 无论您预加载了什么,它都会始终执行另一个查询(至少从Rails 4开始,我怀疑它已经改变了)。这是因为实现了 find_by 可以生成更多的SQL。如果要预加载,则可以使用 find 来代替,只要每个父级的资产数量都不是可笑的,这很好,但是如果有很多资产,则不好很多资产和/或它们是会占用大量内存的大对象(请参阅下面的alt解决方案注释)。

First issue is that adding a find_by will always execute another query no matter what you have preloaded (at least as of Rails 4, I doubt it has changed though). This is because find_by is implemented to generate more SQL. If you want to preload, you can use find instead, which is fine as long as there aren't a ridiculous number of assets per parent, but bad if there are lots and lots of assets and/or they are large objects that would take up a ton of memory (see note below for alt solution).

您可以像这样预加载资产:

You can preload assets like this:

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 关联:

Alternatively, you can declare a has_many :through association:

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中执行查找,则可以定义范围关联:


If you want to execute the find in the db layer and not in ruby, you can define a scoped association:

class Parent < ActiveRecord::Base
  has_one :first_asset, ->{ where asset_type: "first" }
  ...
end

通过这种方式,您可以 preload(:parent =>:first_asset)

This way you can preload(:parent => :first_asset) instead.

这篇关于Rails 5急于加载,然后find_by的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-11 06:51