In my Ruby on Rails application I have a database structure like this:
Project.create(:group => "1", :date => "2014-01-01") Project.create(:group => "1", :date => "2014-01-02") Project.create(:group => "1", :date => "2014-01-03") Project.create(:group => "2", :date => "2014-01-01") Project.create(:group => "2", :date => "2014-01-02") Project.create(:group => "2", :date => "2014-01-03") # and so forth...
How can I get the latest record from each group
using ActiveRecord?
The solution is probably simple but I can't get my head around this.
Thanks for any help.
ActiveRecord is an ORM. It's a layer of Ruby code that runs between your database and your logic code. When you need to make changes to the database, you'll write Ruby code, and then run "migrations" which makes the actual changes to the database.
ActiveRecord::Base indicates that the ActiveRecord class or module has a static inner class called Base that you're extending.
Active Record uses naming conventions for the columns in database tables, depending on the purpose of these columns. Foreign keys - These fields should be named following the pattern singularized_table_name_id (e.g., item_id , order_id ).
The Relation Class. Having queries return an ActiveRecord::Relation object allows us to chain queries together and this Relation class is at the heart of the new query syntax. Let's take a look at this class by searching through the ActiveRecord source code for a file called relation.
In Postgres, this can be achieved with the following query.
SELECT DISTINCT ON ("group") * FROM projects ORDER BY "group", date DESC, id DESC
Because the date
column might not be unique here, I have added an additional ORDER BY
clause on id DESC
to break ties in favor of the record with the higher ID, in case two records in a group have the same date. You might instead want to use another column like the date/time of the last update or so, that depends on your use case.
Moving on, ActiveRecord unfortunately has no API for DISTINCT ON
, but we can still use plain SQL with select
:
Project.select('DISTINCT ON ("group") *').order(:group, date: :desc, id: :desc)
or if you prefer using ARel instead of having raw SQL:
p = Project.arel_table Project.find_by_sql( p.project(p[Arel.star]) .distinct_on(p[:group]) .order(p[:group], p[:date].desc, p[:id].desc) )
For other databases like MySQL this is unfortunately not as convenient. There are a variety of solutions available, see for example this answer.
I spent some time battling this and thought I'd share what I found to be the cleanest and stunningly easy solution (assuming date
or other sorting field contains unique values):
Project.group(:group).maximum(:date)
Hat tip to qarol for posting this in this comment.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With