我正在处理一个项目,并且有一个复杂的查询需要10秒左右才能执行。我意识到发生了N + 1查询,但我是rails的新手,我不知道如何修复它。控制器代码是:
def index
filters = params.slice(:package_type, :guid)
list = packages
list = list.where(filters) unless filters.empty?
respond_to do |format|
format.html { @packages = list.includes(:classification).order(:priority => :asc) }
format.json { @packages = list.includes(:classification, {channels: [:classification, :genres]}, :extras).order(:priority => :asc) }
end
end
包模型
class Package < ActiveRecord::Base
extend FriendlyId
belongs_to :classification
has_many :package_channels
has_many :channels, -> { order(:priority => :asc, :identifier => :asc) }, through: :package_channels
has_many :package_extras
has_many :extras, -> { order(:identifier => :asc) },through: :package_extras
渠道模型有:
class Channel < ActiveRecord::Base
belongs_to :classification
has_many :channel_genres
has_many :genres, through: :channel_genres
has_many :package_channels
has_many :packages, through: :package_channels
我还想提一下过滤器通常是空的。如果我遗漏任何信息,请随时评论,我会添加它。谢谢你的时间!
这是控制器的#packages方法。
def packages
@plan ? @plan.packages : Package
end
以下是视图:index.json.jbuilder
json.cache! ["cache", "#{params["plan_id"]}_packages_index"] do
json.array! @packages do |package|
json.partial! 'packages/package_lean', package: package
end
end
答案 0 :(得分:1)
我没有看到查询本身,所以我可能无法专门针对此案例进行回答。
通常,您的第一步应该是使用预先加载技术来阻止N + 1查询。很可能您正在请求尚未加载的关联集合(或单个对象)。
# controller
def index
@customers = Customer.active
end
# view
<% @customers.each do |c| %>
<%= c.name %> # this is fine the object is already in memory from your controller
<%= c.address %> # this one makes a query to the database
<% end %>
通常可以通过添加includes(association)
来解决此问题。
@customers = Customer.active.includes(:address)
另一个好处是关联的外键索引。
add_index :customer, :address_id
在为某些复杂查询构建执行计划时,数据库引擎可能会选择不使用此索引,但是,对于一个简单的查询,情况就是如此。
有一个名为bullet的badass gem。它会在您开发应用程序时监视您的查询,并在您应该添加急切加载(N + 1个查询)时,当您使用不必要的急切加载以及何时应使用计数器缓存时通知您。