最初の解決策は、クエリを ActiveRecord に変換し、サブクエリを使用することです。
subquery = User.where("users.group_id = groups.id").select('count(1)')
groups_with_count = Group.select(:id, "(#{subquery.to_sql}) as users_count")
または、同じ結果を得るためにSQLグループ化を使用します
groups_with_count = Group.joins(:users).select(:id, 'count(users.id) as users_count').group(:id)
どちらの場合も、MINIMAL raw sql を使用した 1 つのクエリで結果を取得できます。
groups_with_count.each { |group| puts "#{group.id} => #{group.users_count}" }
追記事項
subquery = User.via(:group).select('count(1)')
次のヘルパーを使用して、よりシンプルで保守しやすい最初のサブクエリを作成できます。
より適切なサブクエリを作成するために、いくつかのプロジェクトでこのコードを使用しました。
class ApplicationRecord < ActiveRecord::Base
# transform Raw sql that references an association such as: Shift.where('shifts.id = checkins.shift_id')
# into a simpler version Shift.via(:checkin) if shift have the checkin relationship
# No support for polymorphic association
# Basic support for "through" reflection (using join)
def via(name)
association = reflect_on_association(name)
raise ArgumentError, "#{name} is not a valid association of #{self.class.name}" unless association
raise NotImplementedError if association.polymorphic?
join_keys = association.join_keys
table_column = arel_table[join_keys.foreign_key]
association_column = Arel::Table.new(association.table_name)[join_keys.key]
if association.through_reflection?
through_association = association.through_reflection
table_column = Arel::Table.new(through_association.table_name)[join_keys.foreign_key]
joins(through_association.name).where(table_column.eq(association_column))
else
where(table_column.eq(association_column))
end
end
end