我有两个模型,User
和Group
,其中组包含许多用户。如果我想使用单个查询计算每个组中的用户数,我可以使用以下 SQL:
select id, (select count(1) from users where group_id = groups.id) from groups
是否可以使用 ActiveRecord 有效地做到这一点?
需要明确的是,此查询将列出所有组 ID,以及每个组中的用户数。
我有两个模型,User
和Group
,其中组包含许多用户。如果我想使用单个查询计算每个组中的用户数,我可以使用以下 SQL:
select id, (select count(1) from users where group_id = groups.id) from groups
是否可以使用 ActiveRecord 有效地做到这一点?
需要明确的是,此查询将列出所有组 ID,以及每个组中的用户数。
您可以使用其中任何一个来获取计数
使用关联
group = Group.find(1) #find group with id = 1
group.users.count # count users whose group_id = 1 calls db everytime
or
group.users.size # get size from cache if group.users loaded
或直接
User.where(:group_id=>1).count
计数助手在具有指定条件的数据库上触发计数(*)查询在 http://apidock.com/rails/ActiveRecord/Calculations/count检查更多选项
我也建议你通过rails guides
我找到了一个使用连接的有效解决方案:
Group.all(
:select => "groups.id, count(u.group_id) as users_count",
:joins => "LEFT OUTER JOIN users u ON u.group_id = groups.id",
:group => "groups.id"
)
第一个解决方案是简单地将您的查询转换为 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 原始 sql 在一个查询中获得结果:
groups_with_count.each { |group| puts "#{group.id} => #{group.users_count}" }
您可以使用以下帮助程序将第一个子查询编写subquery = User.via(:group).select('count(1)')
为更简单和可维护的 imo。
为了编写更好的子查询,我在几个项目中使用了这段代码:
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