我很抱歉,如果这已经存在,我看了但是找不到这个确切的问题。
假设我有三个ActiveRecord模型,Continent
,Country
和Town
。 Town
有一个属性population
,我有兴趣通过对各个城镇的人口进行求和来分解每个国家的人口,按大陆分组。
因为一个国家有很多城镇,但是大陆上的国家相对较少,而且只有几个大陆,我可能会继续这样做:
continents = {}
Continent.each do |continent|
country_breakdown = {}
continent.countries.each do |country|
country_breakdown[country] = country.towns.sum(:population)
end
continents[continent] = country_breakdown
end
...然后在视图中迭代continents
。
问题是这有SQL复杂性O(n)
,其中n
是国家/地区。
我的问题是:有没有办法让SQL生成(在单个查询中)这个分组结果,然后我们可以像在Rails视图端的普通嵌套Hash
一样迭代?
我尝试过使用ActiveRecord::QueryMethods#group
,但我对如何迭代结果感到困惑。
答案 0 :(得分:2)
是的,有。假设你完全描述了你的关系:
class Continent
has_many :countries
has_many :towns, through: :countries
end
class Country
belongs_to :continent
has_many :towns
end
class Town
belongs_to :country
has_one :continent, through: :country
end
您应该可以运行:
results = Continent.joins(:town).group(:continent_id, :country_id).sum(:population)
这会以[[continent_id, country_id], population]
格式为您提供哈希值。
e.g. {[2, 38]=>39993, [1, 31]=>127425, [5, 12]=>113556, [5, 76]=>2966, [1, 10]=>263473, [1, 34]=>154492, [2, 37]=>55087...}
通过加入,您可以访问查询中的Town
模型,Continent
因has_many through:
关系而知道该模型。
您可以迭代该哈希以获得您想要的格式的数据:
formatted = results.each_with_object({}) do |((continent_id, country_id), pop), m|
m[continent_id] ||= {}
m[continent_id][country_id] = pop
end
e.g. { continent_id => { country_id => population } }
{2=>{38=>39993, 37=>55087}, 1=>{31=>127425, 10=>263473...}
执行此步骤的方法可能更为有效。将每个条目映射到一个真实对象可能很有用,这样您就不会错过哪个数字指的是哪个值。但无论如何,这会将您的数据库访问减少到一个查询,这将比原始实现快得多。