I have a model (Expense) that contains fields like 'cost'.
I'd like to iterate through all my expenses to find the sum of the cost for all entries belonging to a particular month.
Is there a way to do it in rails directly?
Expense.find(:all, :conditions => .....)
To get the SUM of costs for the month of a given date:
# date = any day of the month of intrest
Expense.sum(:cost, :conditions => {:created_at =>
(date.beginning_of_month..date.end_of_month)})
To get the sum of costs of all the months:
Expense.sum(:cost,
:group => "EXTRACT(YEAR_MONTH FROM created_at)").each do |y_m, cost_sum|
p "#{y_m}-#{cost_sum}"
end
In the above call, use the conditions option to restrict the result-set to a date range.
sum/group_by:
Expense.find(:all,
:select => "SUM(cost) as cost_sum, MONTH(date) as month, YEAR(date) as year",
:group => "MONTH(date), YEAR(date)" )
Have a look at this beautiful gem: https://github.com/radar/by_star
Related
I have a rails app with a PostgreSQL database with hourly price data for the last 10 years, and I need to get the daily average price for each day saved in a new DB table. This query groups records by day and returns an average price for each day:
averages = Sale.average(:price, :group => "DATE_TRUNC('day', date)")
The Rails Console response looks like this (a sample of 2 days of data):
{"2013-01-03 00:00:00"=>#<BigDecimal:7fcc7c2e4a28,'0.1752888888 88888889E2',27(27)>, "2013-01-02 00:00:00"=>#<BigDecimal:7fcc7c2e4848,'0.2547086956 52173913E2',27(27)>}
Can anyone suggest how to write some code to save these returned date and averageprice values to a new Object called DailyAverage?
Your response is like:
{
'date' => BigDecimal,
'date2' => BigDecimal,
'date2' => BigDecimal,
}
So you can access to each average for each date like this:
averages = Sale.average(:price, :group => "DATE_TRUNC('day', date)")
averages.each do |date, avg|
DailyAverage.create(date: date, average: avg.to_f)
end
This code assumes you have a DailyAverage model with date and average attributes.
You may have to use a to_date in the each loop:
averages.each do |date, avg|
DailyAverage.create(date: date.to_date, average: avg.to_f)
end
How can I query activities for a given week except for the day with name equal to the string "summary"?
Activity.where(:week => wk, :day => "Summary").each do |daily_activity|
end
How do you express the negation? And show the activities for each day in the week, but not when the day is "Summary" (as it's not an actual day).
You can't do it directly with a hash, but you can do it with something like the following:
Activity.where(:week => wk).where('day != ?', 'Summary')
If you want expanded options for finders, check out the squeel gem.
Imagine the scenario,
I have a table Day_fee(date(Date),fee(Integer))
I want to have a list with only the months of the table, for example:
Day_fee
date(Date) fee(Int)
----------|---------
2012/03/27 1
2012/03/26 2
2012/03/25 2
2012/05/01 3
2012/05/02 3
2012/07/01 2
2013/04/01 2
2013/05/01 1
The result => a list with 03, 05 and 07 , without repeat the months.
And the other big question:
I would want a hash o list with month and year, for example {"03"=>"2012","04"=>"2013","05"=>"2013", "05"=>"2012", "07"=>"2012"} , without repeat the months, maybe the table can have more years with the same month
Beacuse, after that, i want to show a html table with the months and paginate that.
Thoughts?
Thanks for your help.
Try this:
If you are using Postgres DB
# Postgres
DayFee.sum(:fee, :group => "EXTRACT(MONTH FROM date)")
=> #<OrderedHash {"3" => 5, "5"=> 6, "7"=> 2}
If you are using MySQL
DayFee.sum(:fee, :group => "MONTH(date)")
Edit
Based on your comment, it looks like you want a different data:
Postgres
DayFee.count(:fee, :group => "date_trunc('month', date)").
map{|d, k| Date.parse(d)}.
inject({}){|h, d| h[d.month]=d.year;h}
MySQL
DayFee.count(:fee, :group => "DATE_FORMAT(date, '%Y-%m')").
map{|d, k| Date.parse(d+"-00 00:00")}.
inject({}){|h, d| h[d.month]=d.year;h}
To get a 2D array
DayFee.count(:fee, :group => "date_trunc('month', date)",
:order => "date_trunc('month', date) ASC").
map{|d, k| Date.parse(d)}.
map{|h, d| [d.month, d.year]}
# [["03", "2012"], ["03", "2013"], ["03", "2014"]]
To get hash with collection of years
DayFee.count(:fee, :group => "date_trunc('month', date)").
map{|d, k| Date.parse(d)}.
inject({}){|h, d| (h[d.month]||=[]) << d.year;h}
# {"03" => ["2012", "2013", "2014"]}
The shortest string for Rails 3.2 is DayFee.pluck(:date).map(&:month).uniq but more faster solution depends on the db and for postgres is DayFee.select("EXTRACT(MONTH FROM date) as month").group(:month).map(&:month)
I think you want the GROUP method of ActiveRecord then using the Month() function in mySQL.
Day_fee.select("Month(date) as month, sum(fee) as sum_fee)").group("Month(date)")
This should return an Day_fee object with the three months and the sum of the fee.
If the DayFee table is small, I would use:
DayFee.all.map{ |df| df.date.month }.uniq
If the DayFee table is large, use one of the following:
# Rails 3.2+
# Never used pluck, but I think it works this way
DayFee.select("MONTH(date) as month").group_by(:month).pluck(:month)
# Rails < 3.2
DayFee.connection.select_values "select MONTH(date) as month from day_fees group by month"
Since you're storing the dates, not just month names (numbers), you'd need to get the list of all dates:
DayFee.select(:date).uniq ## active record should generate the correct distinct statement to avoid data redundancy
than you can find the unique months:
DayFee.select(:date).uniq.map{|d| d.gsub(/.+\/(.+)\/.+/, '\1')}.uniq
I have a table with a float called 'cost' and timestamp called'created_at'.
I would like it to output an array with the summing the costs for each particular day in the last month.
Something like:
#newarray = [] #creating the new array
month = Date.today.month # Current Month
year = Date.today.year # Current Year
counter = 1 # First Day of month
31.times do #for each day of the month (max 31)
#adding sales figures for that day
#newarray.push(Order.sum(:cost, :conditions => {:created_at => "#{year}-#{month}-#{counter}"}))
counter = counter + 1 #go onto next day
end
However this doesn't work as all the timestamps have a time as well.
Apologies in advance for the poor title, I can't seem to think of a sensible one.
You should be able to use code like the following:
sales_by_day = Order.sum(:cost,
:group => 'DATE(created_at)',
:conditions => ['DATE(created_at) > ?', 31.days.ago])
(0..30).collect { |d| sales_by_day[d.days.ago.to_date.to_s] || 0 }.reverse
This will sum the order cost by day, then create an array with index 0 being the last 24 hours. It will also take only one query, whereas your example would take 31.
To change the cut off date, replace 31.days.ago with an instance of the Time class. Documentation here: http://ruby-doc.org/core/classes/Time.html
Good luck!
This should work:
(Date.today.beginning_of_month..Date.today.end_of_month).map { |d|
Order.sum(:cost, :conditions => ['created_at >= ? AND created_at < ?', d, d+1])
}
Although I think you should try getting it using a single query, instead of making one for each day.
I have a database with some fields I'd like to sum. But that's not the big problem, I want to group those fields by the month they were created. ActiveRecord automaticaly created a field named "created_at". So my question; how can I group the result by month, then sum the fields for each month?
Updated with code
#hours = Hour.all(:conditions => "user_id = "+ #user.id.to_s,
:group => "strftime('%m', created_at)",
:order => 'created_at DESC')
This is the code I have now. Managed to group by month, but doesn't manage to sum two of my fields, "mins" and "salary" which I need to sum
You can use active record calculations to do this. Some example code might be
Model.sum(:column_name, :group => 'MONTH("created_at")')
Obviously with the caveat MONTH is mysql specific, so if you were developing on an SQLite database this would not work.
I don't know if there's a SQL query you use to do it (without changing your current table structure). However, you do it with some lines of code.
records = Tasks.find(:conditions => {..})
month_groups = records.group_by{|r| r.created_at.month}
month_groups.each do |month, records|
sum stuff.. blah blah blah..
end
I saw this link on the right side of this question. I assume other databases, besides MySQL have similar functions.
mysql select sum group by date
Fixed it by using :select when getting the query, inputing selects manually
#hours = Hour.all(:conditions => "user_id = "+ #user.id.to_s,
:select => "created_at, SUM(time) time",
:group => "strftime('%m', created_at)",
:order => 'created_at DESC')