Медлительность SQL при выполнении поиска ActiveRecord в отношении HABTM - PullRequest
0 голосов
/ 20 января 2011

Я использую Rails 2.3.5 с базой данных MySQL. У меня есть отношение HABTM между Books и Users, и я пытаюсь получить всех пользователей, у которых есть определенный список книг (определенный массивом названий книг).

Я могу выполнить 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
]

Мои вопросы:

  • Кто-нибудь знает, почему это так? Почему double INNER JOIN медленнее, чем мой INNER JOIN с вложенным SELECT запросом?
  • Вызов find_by_sql на самом деле не использует встроенную поддержку, которую Rails предоставляет для отношений HABTM. В частности, он выходит за пределы таблицы соединений books_users, которую поддержка Rails обычно отделяет от разработчика. Есть ли способ указать тот же запрос с помощью вызова find, который скрывает это?

Ответы [ 2 ]

1 голос
/ 20 января 2011

После комментариев выше, похоже, вам нужны индексы для полей book_id и user_id в books_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
0 голосов
/ 20 января 2011

Может ли использование: include vs.: join улучшить объединение?

User.find(
  :all,
  :include    => :books,
  :conditions => { :books => { :name => book_names } }
)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...