Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Group By Year, Month & then Count in ActiveRecord 3

I'm trying to pull a count of all users created in a year, and month but the following doesn't seem to work as expected.

User.group("YEAR(created_AT), MONTH(created_at)").
     count("DISTINCT(id), YEAR(created_at), MONTH(created_at)")

i'm looking for something like

{2011 => {1 => 222, 2 => 333, 4 => 444, 5 => 667 ... }}

but i'm getting

{1 => 222, 2 => 333, 4 => 444, 5 => 667 ... }

Am i missing something, or can ActiveRecord not give me this result in one query?

like image 478
Schneems Avatar asked Dec 12 '11 04:12

Schneems


1 Answers

The count method doesn't work like you think it does. You end up doing this:

select count(distinct(id), year(created_at), month(created_at))
from users
group by year(created_at), month(created_at)

That SELECT clause is pretty dodgy but MySQL will, in its usual sloppy manner, let it through. I think you want this query:

select count(distinct(id)), year(created_at), month(created_at)
from users
group by year(created_at), month(created_at)

I'd probably go straight to select_all like this:

a = User.connection.select_all(%q{
    select count(distinct(id)) as c, year(created_at) as y, month(created_at) as m
    from users
    group by y, m
})

Or you could do it like this:

a = User.connection.select_all(
    User.select('count(distinct(id)) as c, year(created_at) as y, month(created_at) as m').
         group('y, m')
)

Those will give you an array, a, of Hashes with c, y, and m keys like this:

a = [
    { 'c' => '23', 'y' => '2010', 'm' => '11' },
    { 'c' => '1',  'y' => '2011', 'm' =>  '1' },
    { 'c' => '5',  'y' => '2011', 'm' =>  '3' },
    { 'c' => '2',  'y' => '2011', 'm' =>  '4' },
    { 'c' => '11', 'y' => '2011', 'm' =>  '8' }
]

Then a bit of data wrangling is all you need to finish the job:

h = a.group_by { |x| x['y'] }.each_with_object({}) do |(y,v), h|
    h[y.to_i] = Hash[v.map { |e| [e['m'].to_i, e['c'].to_i] }]
end
# {2010 => {11 => 23}, 2011 => {1 => 1, 3 => 5, 4 => 2, 8 => 11}} 
like image 184
mu is too short Avatar answered Sep 29 '22 11:09

mu is too short