我正在尝试按年龄段列出用户数量:
Range : #Users
10-14 : 16
15-21 : 120
22-29 : 312
30-40 : 12131
41-70 : 612
71-120 : 20
我在考虑创建一个静态的哈希数组:
AGE_RANGES = [
{label:"10 - 14", min:10, max:14},
{label:"15 - 21", min:15, max:21},
{label:"22 - 29", min:22, max:29},
{label:"30 - 40", min:30, max:40},
{label:"41 - 70", min:41, max:70},
{label:"71 - 120", min:71, max:120}
]
然后将其用于我的搜索过滤器以及我的查询。但是,我想不出能从中获得最佳表现的方法。
我的模型中的方法仅按年龄分组:
def self.group_by_ageRange(minAge, maxAge)
query = User.group("users.age")
.where("users.age BETWEEN minAge and maxAge ")
.select("users.age,
count(*) as number_of_users")
end
有什么建议吗?
答案 0 :(得分:7)
您希望构建一些如下所示的SQL:
select count(*),
case
when age between 10 and 14 then '10 - 14'
when age between 15 and 21 then '15 - 21'
-- ...
end as age_range
from users
where age between 10 and 120
group by age_range
在ActiveRecord术语中,那将是:
# First build the big ugly CASE, we can also figure out the
# overall max and min ages along the way.
min = nil
max = nil
cases = AGE_RANGES.map do |r|
min = [r[:min], min || r[:min]].min
max = [r[:max], max || r[:max]].max
"when age between #{r[:min]} and #{r[:max]} then '#{r[:min]} - #{r[:max]}'"
end
# Then away we go...
age_ranges = Users.select("count(*) as n, case #{cases.join(' ')} end as age_range")
.where(:age => min .. max)
.group('age_range')
.all
这将为您留下age_ranges
中的一系列对象,这些对象将采用n
和age_range
方法。如果你想要哈希,那么:
age_ranges = Hash[age_ranges.map { |r| [r.age_range, r.n] }]
当然,这不会包括没有任何人的范围;我将把它作为读者的练习。
答案 1 :(得分:2)
我发现接受的答案有点密集。快速但难以理解和写作。今天,我想出了一个更慢但更简单的解决方案。由于我们将年龄分组到范围内,我们可以假设我们不会有values over 125
这意味着如果您对分组和计数的结果集使用ruby过滤器,则不会迭代超过125个项目。这将比基于sql范围的组/计数慢,但它对于我的目的来说足够快,同时仍然依赖于DB来完成大部分繁重的工作。迭代少于125个项目的哈希似乎不是什么大问题。特别是当键值对只是这样的内容时:
{
0 => 0,
1 => 1,
3 => 5,
25 => 3,
99 => 3
}
这是psudo代码:
users = User
.where(age: (min..max))
.group(:age)
.count(:age)
group = Hash.new(0)
users.each{|age, count|
case
when age <= 10
group['under 10'] += count
when age <= 25
group['11-25'] += count
when age <= 40
group['26-40'] += count
else
group['41+'] += count
end
}
注意:此解决方案提供给定范围内的用户数。