如何在选择框中解决N + 1问题?

时间:2014-01-02 16:35:33

标签: sql ruby-on-rails ruby activerecord

在我的Rails应用中,我people可以有很多projects,反之亦然。这两个表由连接表jobs链接。

class Project < ActiveRecord::Base

  belongs_to :user

  has_many :people, :through => :jobs

  def self.names_as_options
    order(:name).map{ |p| [ p.name, p.id, :'data-people_count' => p.people.count ] }
  end

end

在我的一个表格中,我有这个选择框:

<%= f.select :project_id, Project.names_as_options %>

问题是count上的people为每个项目提供了 N + 1 查询。

克服这个问题的最佳方法是什么?

感谢您的帮助。

4 个答案:

答案 0 :(得分:1)

尝试使用范围与lambda,为此,这是一个如何工作的例子:

  scope :top, lambda { order('views DESC').limit(20) }

在控制器中调用

Project.top

这是在Ruby on Rails中过滤结果的最佳方法。

答案 1 :(得分:1)

如果您使用计数,您最好也使用计数器缓存,它们会在需要时自动使用。 http://guides.rubyonrails.org/association_basics.html

答案 2 :(得分:1)

您可以添加&#34;计数器缓存&#34;每个项目的人数。通常,您将通过迁移

向项目表中添加一个字段
add_column :projects, :people_count, :integer, :default => 0

然后声明在Person模型中使用:counter_cache

class Person < ActiveRecord::Base
  belongs_to :projects, :counter_cache => true
end

当你正在进行求职加入时,这可能不会做你想要的事情。所以,Person #project声明只是一个方便的查找器,并没有在任何回调中使用。但是,你明白了。

您可以按照上面的建议添加一个列,然后在Job类中使用一些回调方法。

class Job
  def update_project_counter
    project.update_people_counter
  end
  after_create :update_project_counter
  after_destroy :update_project_counter
end

class Project
  def update_people_counter
    self.update_attribute :people_count, people.count
  end
end

或类似的东西。那么您应该只需要一个查询。

class Project < ActiveRecord::Base
  def self.names_as_options
    order(:name).map do |p|
      [p.name, p.id, :'data-people_count' => p.people_count]
    end
  end
end

答案 3 :(得分:1)

急切加载将解决此问题,请使用“包含”如下。

实施例,

class LineItem < ActiveRecord::Base
  belongs_to :order, -> { includes :customer }
end

class Order < ActiveRecord::Base
  belongs_to :customer
  has_many :line_items
end

class Customer < ActiveRecord::Base
  has_many :orders
end

参考:http://guides.rubyonrails.org/association_basics.html