帮助我了解Rails急切加载

时间:2010-05-14 15:02:00

标签: ruby-on-rails activerecord eager-loading

我对活动记录中急切加载的机制感到有些困惑。假设Book模型有很多Pages,我使用此查询获取一本书:

@book = Book.find book_id, :include => :pages

现在我感到很困惑。我的理解是@book.pages已经加载,不会执行另一个查询。但是假设我想找一个特定的页面,我该怎么办?

@book.pages.find page_id

# OR...

@book.pages.to_ary.find{|p| p.id == page_id}

我是否正确地认为第一个示例将执行另一个查询,因此使得急切加载毫无意义,或者是活动记录聪明到足以知道它不需要再进行另一个查询?

另外,我的第二个问题是,有一种观点认为,在某些情况下,急切加载对数据库的影响更大,有时多个小查询会比单个大型查询更有效吗?

感谢您的想法。

1 个答案:

答案 0 :(得分:2)

当ActiveRecord急切地加载关联时,对象内部深处发生的是设置的实例变量。就是这样,没有魔力。 Enumerable和ActiveRecord :: AssociationProxy提供的所有方法只是查看实例变量以了解它是否已加载,然后继续它的业务。

当你调用#find时,你没有加载集合:你正在搜索特定的实例。你不是在谈论这个系列本身。

你的第二个例子是要走的路,但我会采用不同的方式:

@book.pages.detect {|p| p.id == page_id}

或者,在这里,我假设你的应用程序有些事情,我会以更好的方式做到:

class BooksController < ApplicationController
  def show
    @book = Book.find(params[:id], :include => :pages)
    @pages_by_id = @book.pages.index_by(&:id)
  end
end

# app/views/books/show.html.erb
Page: <%= @pages_by_id[page_id].number %>

注意我正在使用#index_by,它返回一个哈希,其中键是评估块的结果,值是原始对象。由于您似乎只想按ID查找页面,因此有一个哈希是有意义的。

您对数据库或多或少密集的观点是一个很好的观点,并且始终牢记在心。如果你将使用大部分返回的数据,那将是有意义的。如果你只使用一小部分数据,那么下载这些对象的所有数据是没有意义的,只是在几毫秒后将它们扔到垃圾收集中。