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

后端 未结 5 1975
暖寄归人
暖寄归人 2020-12-14 01:07

In my Ruby on Rails application I have a database structure like this:

Project.create(:group => \"1\", :date => \"2014-01-01\")
Project.create(:group =         


        
相关标签:
5条回答
  • 2020-12-14 01:22

    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.

    0 讨论(0)
  • 2020-12-14 01:31

    This works for me

    ids = Message.select("MAX(id) AS id").group(:column_name).collect(&:id)
    @result = Message.order("created_at DESC").where(:id => ids)
    
    0 讨论(0)
  • 2020-12-14 01:31

    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.

    0 讨论(0)
  • 2020-12-14 01:31
    Project.where(:group => "1", :date => "2014-01-01").last
    

    .last is what you are looking for.

    0 讨论(0)
  • 2020-12-14 01:35

    Something like this?

    Project.select(:group).map(&:group).uniq.each do |grp|
      puts Project.where(group: grp).order("date DESC").last
    end
    

    This will go through all your groups and identify the unique ones. In your example it should return ["1", "2"]. Then it iterates over that array and selects the last Project with a group id of 1 and the last Project with a group id of 2.

    ** Update **

    Just realized you said "latest" and not "last" which required adding an order to ensure latest works. Last still pulls just one.

    0 讨论(0)
提交回复
热议问题