Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get the latest record from each group in ActiveRecord?

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.

like image 343
Tintin81 Avatar asked Jan 22 '14 17:01

Tintin81


People also ask

Is ActiveRecord an ORM?

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.

What does ActiveRecord base do?

ActiveRecord::Base indicates that the ActiveRecord class or module has a static inner class called Base that you're extending.

What is ActiveRecord naming convention?

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

What is an ActiveRecord relation object?

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.


2 Answers

Postgres

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

MySQL

For other databases like MySQL this is unfortunately not as convenient. There are a variety of solutions available, see for example this answer.

like image 115
Patrick Oscity Avatar answered Sep 28 '22 16:09

Patrick Oscity


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.

like image 30
jstafford Avatar answered Sep 28 '22 14:09

jstafford