Запрос Rails 3 при условии подсчета ассоциации
В Rails 3 с mysql предположим, что у меня есть две модели, клиенты и покупки, очевидно, покупают own_to клиент. Я хочу найти всех клиентов с 2 заказами и более. Я могу просто сказать:
Customer.includes(:purchases).all.select{|c| c.purchases.count > 2}
Эффективно, однако, строка выше делает запрос о величине Customer.all и Purchase.all, затем обрабатывает тип "select" в ruby. В большой базе данных я бы предпочел избегать выполнения всего этого "выбора" расчета в рубине, а mysql выполнить обработку и предоставить только список квалифицированных клиентов. Это намного быстрее (поскольку mysql более настроен для этого) и значительно снижает пропускную способность из базы данных.
К сожалению, я не могу вызвать код со строительными блоками в rails (где, имея, группу и т.д.), чтобы это произошло, что-то в строках (psudo-code):
Customer.joins(:purchases).where("count(purchases) > 2").all
Я соглашусь на прямое решение MySql, хотя я предпочитаю это разбираться в элегантной структуре рельсов.
Ответы
Ответ 1
Документация по этому вопросу на данном этапе довольно редкая. Я бы рассмотрел использование Metawhere, если вы будете делать больше запросов, похожих на это. Используя Metawhere, вы можете сделать это (или что-то подобное, не уверен, что синтаксис точно верен):
Customer.includes(:purchases).where(:purchases => {:count.gte => 2})
Красота этого заключается в том, что MetaWhere все еще использует ActiveRecord и isl для выполнения запроса, поэтому он работает с "новыми" рельсами 3 способами выполнения запросов.
Кроме того, вы, вероятно, не хотите вызывать .all
в конце, так как это вызовет запрос для ping базы данных. Вместо этого вы хотите использовать ленивую загрузку и не ударять db, пока вам не понадобятся данные (в представлении или какой-либо другой метод, который обрабатывает фактические данные.)
Ответ 2
Не нужно устанавливать драгоценный камень, чтобы заставить его работать (хотя метафора крутая)
Customer.joins(:purchases).group("customers.id").having("count(purchases.id) > ?",0)
Ответ 3
Это немного более подробный, но если вы хотите, чтобы клиенты where count = 0
или более гибкий sql, вы сделали бы LEFT JOIN
Customer.joins('LEFT JOIN purchases on purchases.customer_id = customers.id').group('customers.id').having('count(purchases.id) = 0').length