Я использую 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
, который скрывает это?