简体   繁体   中英

Aggregate Event cost by month when Event ranges more than one month (in rails 3)

I have this Event model

class Event < ActiveRecord::Base
  attr_accessible :starts_at, :ends_at, :price
end

An Event can be a single instant event:

Event.create(:starts_at => Date.today, :ends_at=>nil, :price => 10.0)

It can also span multiple days or months:

Event.create(:starts_at => Date.today, :ends_at => (Date.today + 2.months), :price => 20.0)

I want to break down the cost of events by months, so that cost for an instant event falls into the month it belongs to, naturally, and cost for an event that spans multiple months should be divided proportionally between those months.

Obviously this would be difficult to handle using SQL, but maybe someone has some advice on that?

What would be the most efficient way of calculating this aggregation?

Update:

Here is a clearly defined structure for the data on sqlfiddle: http://sqlfiddle.com/#!12/a532e/6

What I would want from this dataset is something like:

( (Date('2013-01-01'), 31.6), (Date('2013-02-01'), 8.4) )
SELECT event_id
      ,date_trunc('month', day)::date AS month
      ,sum(daily) AS price_this_month
FROM  (
   SELECT event_id
         ,generate_series(starts_at
                         ,COALESCE(ends_at, starts_at)
                         ,interval '1 day') AS day
         ,price / COALESCE((ends_at - starts_at) + 1, 1) AS daily
   FROM   events
   ) a
GROUP  BY 1,2
ORDER  BY 1,2;

-> sqlfiddle

I provided an updated sqlfiddle for PostgreSQL. Your original seems to be for MySQL.

The tricks are:

  • Use generate_series() to create one row per day for every event.
  • Use COALESCE to take care of NULL values, which seem to be allowed for ends_at .
  • Calculate the daily cost by dividing the price by the number of days between starts_at and ends_at + 1 to fix off-by-one. Default to 1 in case of ends_at IS NULL .
  • Re-aggregate per event and month.

Voilá.

You even get exact rates per month, depending on how many days a month has. February (28 days) is cheaper than January (31 days).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM