如何从ActiveRecord中获取每组的最新记录?

Tin*_*n81 27 ruby activerecord ruby-on-rails

在我的Ruby on Rails应用程序中,我有一个像这样的数据库结构:

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...
Run Code Online (Sandbox Code Playgroud)

如何group使用ActiveRecord 获取每个记录的最新记录?

解决方案可能很简单,但我无法理解这一点.

谢谢你的帮助.

Pat*_*ity 51

Postgres的

在Postgres中,可以使用以下查询来实现.

SELECT DISTINCT ON ("group") * FROM projects
ORDER BY "group", date DESC, id DESC
Run Code Online (Sandbox Code Playgroud)

因为这里的date列可能不是唯一的,所以我添加了一个附加ORDER BY条款id DESC来打破关系,支持具有更高ID的记录,以防组中的两个记录具有相同的日期.您可能希望使用其他列,例如上次更新的日期/时间,这取决于您的用例.

继续,ActiveRecord遗憾的是没有API DISTINCT ON,但我们仍然可以使用纯SQL select:

Project.select('DISTINCT ON ("group") *').order(:group, date: :desc, id: :desc)
Run Code Online (Sandbox Code Playgroud)

或者如果您更喜欢使用ARel而不是使用原始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)
)
Run Code Online (Sandbox Code Playgroud)

MySQL的

对于像MySQL这样的其他数据库,遗憾的是这并不方便.有多种解决方案可供选择,例如参见本答案.

  • 对于**PostgresSQL**,您可以使用`Project.select("DISTINCT ON(group_id)*").order("group_id,date DESC")` (16认同)
  • @Alain确实如此.我认为更好的解决方案是这样的想法:`Project.group("group").maximum(:date)`.它应该返回每个组的最大日期而不省略任何组. (6认同)
  • @ p11y这正是我所寻找的,但是,它在PostgreSQL中不起作用.我得到一个PGError - "column"projects.id"必须出现在GROUP BY子句中或者用在聚合函数中." 有任何想法吗? (2认同)

jst*_*ord 7

我花了一些时间来解决这个问题,并认为我会分享我发现的最干净、最简单的解决方案(假设date或其他排序字段包含唯一值):

Project.group(:group).maximum(:date)
Run Code Online (Sandbox Code Playgroud)

帽尖到qarol在此发布此评论

  • 这绝对很简单,但问题是“我怎样才能获得最新的记录”。如果用户需要扩展此答案以包含其他字段,那就是一个问题。 (4认同)

Nav*_*jad 6

这对我有用

ids = Message.select("MAX(id) AS id").group(:column_name).collect(&:id)
@result = Message.order("created_at DESC").where(:id => ids)
Run Code Online (Sandbox Code Playgroud)


jin*_*ing 5

以下基于此链接的解决方案适用于 MySQL,并且可扩展到表中的所有字段。

Project.select(:group, 'MAX(date) AS date').group(:group)
Run Code Online (Sandbox Code Playgroud)