2012-08-23 35 views
5

Tôi đang cố gắng để liệt kê số lượng người dùng theo độ tuổi tầm:Nhóm Người dùng bởi Tuổi cấp ở ruby ​​

Range : #Users 
10-14 : 16 
15-21 : 120 
22-29 : 312 
30-40 : 12131 
41-70 : 612 
71-120 : 20 

Tôi đã nghĩ đến việc tạo ra một mảng tĩnh của băm:

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} 
] 

và sau đó sử dụng nó cho bộ lọc tìm kiếm của tôi, cũng như cho truy vấn của tôi. Nhưng, tôi không thể nghĩ ra một cách để có được hiệu suất cao nhất từ ​​nó.

phương pháp của tôi trong mô hình của tôi chỉ nhóm theo độ tuổi:

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 

Bất kỳ lời đề nghị?

+1

http://stackoverflow.com/questions/232387/in-sql-how-can-you-group-by-in-ranges – InternetSeriousBusiness

Trả lời

7

Bạn muốn xây dựng một số SQL trông như thế này:

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 

Về ActiveRecord, đó sẽ là:

# 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 

Điều đó sẽ để lại cho bạn với một mảng của các đối tượng trong age_ranges và những đối tượng sẽ có các phương thức nage_range. Nếu bạn muốn một Hash ra khỏi đó, sau đó:

age_ranges = Hash[age_ranges.map { |r| [r.age_range, r.n] }] 

Điều đó sẽ không bao gồm các phạm vi không có bất kỳ người nào trong số đó; Tôi sẽ để nó như một bài tập cho người đọc.

+0

điều này giải quyết được vấn đề của tôi, cảm ơn bạn. Tôi bây giờ dường như có một số lỗi rails bên trong bộ điều khiển ... Nếu tôi thêm 'logger.debug (" items: # {@ ageRange_items.inspect} ")' tất cả đều ổn ... Nếu không, nó chỉ đặt 'cases.join ('')' như 'age_range', đưa ra một lỗi tự nhiên nói' cột age_range không tồn tại ' – MrWater

+0

@itsalltime: Thật khó để chẩn đoán mà không thấy mã cuối cùng. –

+0

ở đây nó đi ... nó phức tạp hơn một chút so với câu hỏi tôi đăng, vì nó có tham gia chuỗi, nhưng nó không nên có hành vi này 'query = User.joins (cửa hàng: {receipts: {tag:: user} }) .select ("trường hợp # {cases.join ('')} kết thúc dưới dạng age_range, đếm (*) là number_of_users, tổng (tổng) là tổng") .where ("users.id =: user_id" , user_id: user) .where ("users_tags.age" => min .. max) .nhóm ("age_range") ' – MrWater

0

Tôi thấy câu trả lời được chấp nhận là hơi dày đặc. Nhanh nhưng khó hiểu và viết. Hôm nay, tôi đã đưa ra một giải pháp chậm hơn nhưng đơn giản hơn. Vì chúng tôi nhóm các độ tuổi thành các phạm vi, chúng tôi có thể giả định rằng chúng tôi sẽ không có values over 125

Điều đó có nghĩa là nếu bạn sử dụng bộ lọc ruby ​​trên nhóm kết quả được tính và nhóm, bạn sẽ không lặp lại quá 125 mục. Điều này sẽ chậm hơn so với một nhóm/số đếm dựa trên sql, nhưng nó đủ nhanh cho mục đích của tôi trong khi vẫn dựa vào DB cho phần lớn việc nâng hạng nặng. Lặp lại trên một băm với ít hơn 125 mặt hàng không có vẻ giống như một việc lớn. Đặc biệt là khi các cặp giá trị key chỉ ints như thế này:

{ 
    0 => 0, 
    1 => 1, 
    3 => 5, 
    25 => 3, 
    99 => 3 
} 

Đây là psudo mã:

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 
} 

Lưu ý: giải pháp này cung cấp số lượng người dùng trong một phạm vi nhất định.

Các vấn đề liên quan