【问题标题】:How to get the latest record from each group in ActiveRecord?如何从 ActiveRecord 中的每个组中获取最新记录?
【发布时间】:2014-01-22 17:04:57
【问题描述】:

在我的 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...

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

解决方案可能很简单,但我无法理解。

感谢您的帮助。

【问题讨论】:

  • 您希望它们按其date 字段还是按创建记录的时间排序?
  • @Agis:通过他们的date 字段。
  • 如果其他人到达此页面只是为了寻找如何按单列上的最新记录排序(无分组),只需执行Project.order(created_at: :desc).first 之类的操作。

标签: ruby-on-rails ruby activerecord


【解决方案1】:

Postgres

在 Postgres 中,这可以通过以下查询来实现。

SELECT DISTINCT ON ("group") * FROM projects
ORDER BY "group", date DESC, id DESC

因为date 列在这里可能不是唯一的,所以我在id DESC 上添加了一个额外的ORDER BY 子句,以打破关系,支持ID 较高的记录,以防组中的两条记录具有同一日期。您可能希望使用另一列,例如上次更新的日期/时间等,这取决于您的用例。

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

Project.select('DISTINCT ON ("group") *').order(:group, date: :desc, id: :desc)

或者如果您更喜欢使用 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)
)

MySQL

不幸的是,对于 MySQL 等其他数据库来说,这并不方便。有多种可用的解决方案,例如参见this answer

【讨论】:

  • 谢谢。这正是我要找的。​​span>
  • @p11y 这正是我一直在寻找的,但是,它在 PostgreSQL 中不起作用。我得到一个 PGError - column "projects.id" must appear in the GROUP BY clause or be used in an aggregate function。有什么想法吗?
  • 对于 PostgresSQL 你可以使用Project.select("DISTINCT ON(group_id) *").order("group_id, date DESC")
  • 这不是只有在所有最新记录都具有相同日期值的情况下才有效吗?
  • @Alain 确实如此。我认为更好的解决方案是这样的:Project.group("group").maximum(:date)。它应该返回每个组的最大日期而不省略任何组。
【解决方案2】:

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

Project.group(:group).maximum(:date)

感谢qarolthis comment 上发帖。

【讨论】:

  • 这绝对是简单的,但问题是“我怎样才能得到最新的记录”。如果用户需要扩展此答案以包含其他字段,这是一个问题。
  • 老兄成功了,非常感谢
【解决方案3】:

这对我有用

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

【讨论】:

  • 小心这个解决方案。如果 DB 返回超过 ~1'000 个 id,这可能会导致查询持续时间很长。
【解决方案4】:

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

Project.select(:group, 'MAX(date) AS date').group(:group)

【讨论】:

  • 这个。运行完美且速度非常快。
【解决方案5】:

这样的?

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

这将遍历您的所有组并确定唯一的组。在您的示例中,它应该返回 ["1", "2"]。然后它遍历该数组并选择组 id 为 1 的最后一个项目和组 id 为 2 的最后一个项目。

** 更新**

刚刚意识到您说的是“最新”而不是“最后”,这需要添加订单以确保最新作品。最后仍然只拉一个。

【讨论】:

  • 这也会从数据库中检索所有记录,然后在内存中过滤它们,最后对每个组进行另一个查询。
【解决方案6】:
Project.where(:group => "1", :date => "2014-01-01").last

.last 是您要查找的内容。

【讨论】:

  • 好的,谢谢。如果我不想指定 group 并从 all 组中获取最新条目怎么办?
  • 去掉where条件即可。
  • 这既不提供查找组中最新项目的通用方法,也不返回所有最新项目。
  • 无论分组如何,这只会返回最后一个值
猜你喜欢
  • 2017-02-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-11-22
  • 1970-01-01
  • 2018-06-14
  • 2016-11-26
相关资源
最近更新 更多