【问题标题】:How to select each model which has the maximum value of an attribute for any given value of another attribute?对于另一个属性的任何给定值,如何选择具有属性最大值的每个模型?
【发布时间】:2016-04-21 12:21:41
【问题描述】:

我有一个带有video_iduser_id 和其他一些简单字段的Work 模型。我需要在页面上显示最后 12 个作品,但每个用户只需要 1 个。目前我正在尝试这样做:

 def self.latest_works_one_per_user(video_id=nil)
   scope = self.includes(:user, :video)
   scope = video_id ? scope.where(video_id: video_id) : scope.where.not(video_id: nil)
   scope = scope.order(created_at: :desc)
   user_ids = works = []
   scope.each do |work|
     next if user_ids.include? work.user_id
     user_ids << work.user_id
     works << work
     break if works.size == 12
   end
   works
 end

但我敢肯定,有一种更优雅、更快捷的方法,尤其是当作品数量越来越多时。

【问题讨论】:

  • 能否将问题重述为“显示最近 12 位创作过一件或多件作品的用户的作品 1?”
  • 你可以这样说。但是我无法从 User 模型中判断出用户最近有没有创作过任何作品。
  • 您使用的是什么数据库?使用更复杂 SQL 的解决方案可能是特定于数据库的。
  • 另外,作为记录,您的内存实现中有一个错误:user_idsworks 被初始化为相同的 Array;它们都应该被初始化为单独的Arrays。如果您想要不同的实现,这并不重要。

标签: sql ruby-on-rails ruby ruby-on-rails-4 rails-activerecord


【解决方案1】:

这里的解决方案应该适用于任何 SQL 数据库,只需进行最少的调整。是否认为它优雅取决于您对 SQL 的喜爱程度。

def self.latest_works_one_per_user(video_id=nil)
   scope = includes(:user, :video)
   scope = video_id ? scope.where(video_id: video_id) : scope.where.not(video_id: nil)
   scope.
     joins("join (select user_id, max(created_at) created_at
                    from works group by created at) most_recent
                  on works.user_id = most_recent.user_id and
                    works.created_at = most_recent.created_at").
     order(created_at: :desc).limit(12)
end

但是,它仅在 user_id 和 created_at 的组合是唯一的情况下才有效。如果该组合不是唯一的,您将获得超过 12 行。

在 MySQL 中可以更简单地完成。 MySQL 解决方案在 Postgres 中不起作用,我不知道在 Postgres 中有更好的解决方案,尽管我确信有一个。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-10-14
    • 2021-03-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-01-29
    相关资源
    最近更新 更多