在ActiveRecord上查找HABTM关系时SQL缓慢

时间:2011-01-20 04:58:08

标签: mysql ruby-on-rails

我正在使用MySQL数据库运行Rails 2.3.5。我在BooksUsers之间有一个HABTM关系,并且我试图让所有拥有指定书籍列表的用户(由一系列图书名称定义)。

我可以执行find调用来检索此用户列表:

User.find(
  :all,
  :joins      => :books,
  :conditions => { :books => { :name => book_names } }
)

然而,事实证明这是非常缓慢的。在SQL中玩游戏后,我发现以下调用的速度更快,并检索相同的结果:

User.find_by_sql([
  "SELECT users.* FROM users
   INNER JOIN books_users ON users.id = books_users.user_id
   WHERE books_users.book_id IN (SELECT id FROM books WHERE books.name IN (?))",
  book_names
])

对于同一个查询,find调用在我的计算机上大约需要3000毫秒,而find_by_sql调用大约需要200毫秒;这是一个完整的速度差异。我怀疑罪魁祸首与原始find调用被转换为双INNER JOIN SQL查询这一事实有关,相当于以下内容:

[
  "SELECT users.* FROM users
   INNER JOIN books_users ON users.id = books_users.user_id
   INNER JOIN books ON books_users.book_id = books.id
   WHERE books.name IN (?)",
  book_names
]

我的问题是:

  • 有谁知道为什么会这样?为什么双INNER JOIN比我的单INNER JOIN慢,并且嵌套SELECT查询?
  • find_by_sql调用并未真正利用Rails为HABTM关系提供的内置支持。特别是,它正在显示Rails支持的books_users连接表通常从开发人员那里抽象出来。有没有办法使用隐藏此内容的find调用来指定相同的查询?

2 个答案:

答案 0 :(得分:1)

在上述评论之后,您似乎需要book_iduser_idbooks_users字段的索引。

class AddIndices < ActiveRecord::Migration
  def self.up
    add_index :books_users, :book_id
    add_index :books_users, :user_id
  end

  def self.down
    remove_index :books_users, :book_id
    remove_index :books_users, :user_id
  end
end

答案 1 :(得分:0)

使用:include vs.:join更好地加入?

User.find(
  :all,
  :include    => :books,
  :conditions => { :books => { :name => book_names } }
)