Подтвердить что ты не робот

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
4b9b3361

Ответ 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

Как добавить метод в пользовательскую модель, которая выполняет запрос?

Вы бы не изменяли структуру таблицы, или вы не можете ее изменить?