Select, group and sum results from database - ruby-on-rails

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')

Related

activerecord comparing times

i am trying to do a query that will be compare the time stored in the database to the current time, and if it is greater than today to display those records.
below is query i am currently using that isnt displaying any records
#schedules = Schedule.where(:team_id => current_user[:team_id], :event => '1', :time => ">= Time.now.zone")
how do i go back query against a timestamp? so that these records will be displayed?
have also tried the following
#schedules = Schedule.find_all_by_team_id_and_event_and_time(current_user[:team_id],"1", :time)
#schedules = Schedule.where("team_id = ? and event = ? and time >= ?", [current_user[:team_id], "1", Time.zone.now])
The string is used directly in the SQL query so you need to make sure the column names are correct and unambiguous (if you joined on another table that also has a team_id colum, you would need to do schedules.team_id = ? and ...)

Simple ActiveRecord Question

I have a database model set up such that a post has many votes, a user has many votes and a post belongs to both a user and a post. I'm using will paginate and I'm trying to create a filter such that the user can sort a post by either the date or the number of votes a post has. The date option is simple and looks like this:
#posts = Post.paginate :order => "date DESC"
However, I can't quite figure how to do the ordering for the votes. If this were SQL, I would simply use GROUP BY on the votes user_id column, along with the count function and then I would join the result with the posts table.
What's the correct way to do with with ActiveRecord?
1) Use the counter cache mechanism to store the vote count in Post model.
# add a column called votes_count
class Post
has_many :votes
end
class Vote
belongs_to :post, :counter_cache => true
end
Now you can sort the Post model by vote count as follows:
Post.order(:votes_count)
2) Use group by.
Post.select("posts.*, COUNT(votes.post_id) votes_count").
join(:votes).group("votes.post_id").order(:votes_count)
If you want to include the posts without votes in the result-set then:
Post.select("posts.*, COUNT(votes.post_id) votes_count").
join("LEFT OUTER JOIN votes ON votes.post_id=posts.id").
group("votes.post_id").order(:votes_count)
I prefer approach 1 as it is efficient and the cost of vote count calculation is front loaded (i.e. during vote casting).
Just do all the normal SQL stuff as part of the query with options.
#posts = Post.paginate :order => "date DESC", :join => " inner join votes on post.id..." , :group => " votes.user_id"
http://apidock.com/rails/ActiveRecord/Base/find/class
So I don't know much about your models, but you seem to know somethings about SQL so
named scopes: you basically just put the query into a class method:
named_scope :index , :order => 'date DESC', :join => .....
but they can take parameters
named_scope :blah, {|param| #base query on param }
for you, esp if you are more familiar with SQL you can write your own query,
#posts = Post.find_by_sql( <<-SQL )
SELECT posts.*
....
SQL

Elegant Summing/Grouping/Etc in Rails

I have a number of objects which are associated together, and I'd like to layout some dashboards to show them off. For the sake of argument:
Publishing House - has many books
Book - has one author and is from one, and goes through many states
Publishing House Author - Wrote many
books
I'd like to get a dashboard that said:
How many books a publishing house put
out this month?
How many books an
author wrote this month?
What state (in progress, published) each of the books are in?
To start with, I'm thinking some very simple code:
#all_books = Books.find(:all, :joins => [:author, :publishing_house], :select => "books.*, authors.name, publishing_houses.name", :conditions => ["books.created_at > ?", #date])
Then I proceed to go through each of the sub elements I want and total them up into new arrays - like:
#ph_stats = {}
#all_books.map {|book| #ph_stats[book.publishing_house_id] = (#ph_stats[book.publishing_house_id] || 0) + 1 }
This doesn't feel very rails like - thoughts?
I think your best bet is to chain named scopes together so you can do things like:
#books = Books.published.this_month
http://api.rubyonrails.org/classes/ActiveRecord/NamedScope/ClassMethods.html#M001683
http://m.onkey.org/2010/1/22/active-record-query-interface
You should really be thinking of the SQL required to write such a query, as such, the following queries should work in all databases:
Number of books by publishing house
PublishingHouse.all(:joins => :book, :select => "books.publishing_house_id, publishing_houses.name, count(*) as total", :group => "1,2")
Number of books an author wrote this month
If you are going to move this into a scope - you WILL need to put this in a lambda
Author.all(:joins => :books, :select => "books.author_id, author.name, count(*) as total", :group => "1,2", :conditions => ["books.pub_date between ? and ?", Date.today.beginning_of_month, Date.today.end_of_month])
this is due to the use of Date.today, alternatively - you could use now()::date (postgres specific) and construct dates based on that.
Books of a particular state
Not quite sure this is right wrt your datamodel
Book.all(:joins => :state, :select => "states.name, count(*) as total", :group => "1")
All done through the magic of SQL.

Finding entries belonging to current month (or other months)

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

Group and count in Rails

I know I've seen this before but I can't find anything now. I want to group a query by a certain column and be able to display how many are in each group. I got the first part down:
#line_items = #project.line_items.all(:group => "device_id")
This is for my line item index view, which is just a table displaying the line items. How do I make a column in that table for "count" now that the line items are grouped by device?
You can do count on line_items which will return you an ordered hash of device_id and count.
#project.line_items.group(:device_id).count
hash of devise_id as key and associated records count
#project.line_items.group(:device_id).count
I think you can try this as well.
#project.line_items.group(:device_id).pluck("device_id, count(device_id)")
^^ This gives array of arrays with elements 'device_id and count'
Just add a :select option:
#line_items = #project.line_items.all(
:group => "device_id",
:select => "device_id, COUNT(*) as count"
)
Then each #line_item will have a count attribute.
something like
User.all(:joins => :comments, :select => "users.*, count(comments.id) as comments_count", :group => "users.id")
might also work...
After this commit:
https://github.com/rails/rails/commit/a1c05dd8b9bd3623289d3aa73dda2943d620cc34
there's a new way to do the same thing:
#project.line_items.count(:group => LineItem.arel_table[:device_id])
For only count pluck would be faster here rather than group
#project.line_items.pluck(:device_id).count
#project.line_items.pluck(:device_id).uniq.count

Resources