4

I have a donations table where I'm trying to calculate the total amount for each month. For months without without any donations, I'd like the result to return 0.

Here's my current query:

Donation.calculate(:sum, :amount, :conditions => { 
  :created_at => (Time.now.prev_year.all_year) }, 
  :order => "EXTRACT(month FROM created_at)", 
  :group => ["EXTRACT(month FROM created_at)"])

which returns:

{7=>220392, 8=>334210, 9=>475188, 10=>323661, 11=>307689, 12=>439889}

Any ideas how to grab the empty months?

mu is too short
  • 426,620
  • 70
  • 833
  • 800
tbrooks
  • 85
  • 1
  • 9

2 Answers2

5

Normally you'd left join to a calendar table (or generate_series in PostgreSQL) to get the missing months but the easiest thing with Rails would be to merge your results into a Hash of zeroes; something like this:

class Donation
  def self.by_month
    h = Donation.calculate(:sum, :amount, :conditions => { 
      :created_at => (Time.now.prev_year.all_year) }, 
      :order => "EXTRACT(month FROM created_at)", 
      :group => ["EXTRACT(month FROM created_at)"])
    Hash[(1..12).map { |month| [ month, 0 ] }].merge(h)
  end
end

then just call the class method, h = Donation.by_month, to get your results.

mu is too short
  • 426,620
  • 70
  • 833
  • 800
2

In addition to mu is too short answer, in Rails 3.2.12 did not work for me, ActiveRecord returns the keys as strings:

h = Donation.calculate(:sum, :amount, :conditions => { 
    :created_at => (Time.now.prev_year.all_year) }, 
    :order => "EXTRACT(month FROM created_at)", 
    :group => ["EXTRACT(month FROM created_at)"])

Which returns:

{"7"=>220392, "8"=>334210, "9"=>475188, "10"=>323661, "11"=>307689, "12"=>439889}

So when I merge the hash with zeros:

{1=>0, 2=>0, 3=>0, 4=>0, 5=>0, 6=>0, 7=>0, 8=>0, 9=>0, 10=>0, 11=>0, 12=>0, "7"=>220392, "8"=>334210, "9"=>475188, "10"=>323661, "11"=>307689, "12"=>439889}

The little fix (to_s):

Hash[(1..12).map { |month| [ month.to_s, 0 ] }].merge(h)
Edu Lomeli
  • 2,263
  • 20
  • 18