ActiveRecord Association выбирает количество включенных записей
Пример
class User
has_many :tickets
end
Я хочу создать ассоциацию, которая содержит логику подсчета билетов пользователя и использовать ее в include (user has_one ticket_count)
Users.includes(:tickets_count)
Я пробовал
has_one :tickets_count, :select => "COUNT(*) as tickets_count,tickets.user_id " ,:class_name => 'Ticket', :group => "tickets.user_id", :readonly => true
User.includes(:tickets_count)
ArgumentError: Unknown key: group
В этом случае запрос ассоциации в include должен использовать count with group by...
Как реализовать это с помощью рельсов?
Обновление
- Я не могу изменить структуру таблицы
- Я хочу, чтобы AR генерировал 1 запрос для сбора пользователей с включенными
Update2
Я знаю SQL, я знаю, как выбрать это с помощью объединений, но мой вопрос теперь похож на "Как получить данные". Мой вопрос о строительной ассоциации, которую я могу использовать в составе. Благодаря
Update3
Я попытался создать ассоциацию, созданную как user has_one ticket_count, но
- выглядит как has_one не поддерживает расширения ассоциации
- has_one не поддерживает: опция group
- has_one не поддерживает finder_sql
Ответы
Ответ 1
Попробуйте следующее:
class User
has_one :tickets_count, :class_name => 'Ticket',
:select => "user_id, tickets_count",
:finder_sql => '
SELECT b.user_id, COUNT(*) tickets_count
FROM tickets b
WHERE b.user_id = #{id}
GROUP BY b.user_id
'
end
Edit:
Похоже, что ассоциация has_one
не поддерживает параметр finder_sql
.
Вы можете легко достичь того, чего хотите, используя комбинацию методов scope
/class
class User < ActiveRecord::Base
def self.include_ticket_counts
joins(
%{
LEFT OUTER JOIN (
SELECT b.user_id, COUNT(*) tickets_count
FROM tickets b
GROUP BY b.user_id
) a ON a.user_id = users.id
}
).select("users.*, COALESCE(a.tickets_count, 0) AS tickets_count")
end
end
Теперь
User.include_ticket_counts.where(:id => [1,2,3]).each do |user|
p user.tickets_count
end
Это решение имеет последствия для производительности, если у вас есть миллионы строк в таблице tickets
. Вы должны рассмотреть возможность фильтрации набора результатов JOIN, предоставив WHERE
внутреннему запросу.
Ответ 2
Вы можете просто использовать для определенного пользователя:
user.tickets.count
Или, если вы хотите, чтобы это значение автоматически кэшировалось Rails.
Объявить параметр counter_cache = > true на другой стороне ассоциации
class ticket
belongs_to :user, :counter_cache => true
end
Вам также нужен столбец в вашей пользовательской таблице с именем ticket_count.
С этим каждый раз, когда вы добавляете новые билеты в пользовательские рельсы, он обновляет этот столбец, поэтому, когда вы запишете свою учетную запись пользователя, вы можете просто присоединиться к этому столбцу, чтобы получить счетчик билетов без дополнительного запроса.
Ответ 3
Не очень, но он работает:
users = User.joins("LEFT JOIN tickets ON users.id = tickets.user_id").select("users.*, count(tickets.id) as ticket_count").group("users.id")
users.first.ticket_count
Ответ 4
Как добавить метод в пользовательскую модель, которая выполняет запрос?
Вы бы не изменяли структуру таблицы, или вы не можете ее изменить?