Rails 3:.group()由created_at排序

时间:2011-11-04 15:25:34

标签: ruby-on-rails-3 activerecord group-by sql-order-by

什么是Rails 3的订购方式.group()结果是Activerecord(这里是“created_at”)?

@messages = Message.group(:foo)

仅导致显示最早的消息。我需要最新的展示。

我试过

@messages = Message.group(:foo).having("created_at = MAX(created_at)")

没有成功。任何提示赞赏!

澄清:我希望在自己内部订购该组,而不是正常的messages.order(“...”)。 如果没有简单的Activerecord语法,我也会对原始SQL感到满意


更新:尝试SQL方式,这应该有效:

@messages = Message.find_by_sql("
  SELECT messages.* 
  FROM messages 
  GROUP BY messages.foo 
  HAVING messages.created_at = MAX(messages.created_at) 
  ORDER BY messages.created_at DESC")

但是这只检索单个记录(未分组的记录)。据推测,分组的那些被省略。不知道为什么,所有记录都有:created_at和:foo值

5 个答案:

答案 0 :(得分:6)

您不能在某些数据库下的同一查询中使用order then group,因此您需要使用两个查询来执行此操作:

message_ids = Message.select("MAX(id) AS id").group(:foo).collect(&:id)
@messages = Message.order("created_at DESC").where(:id => message_ids)

注意:这假设您有一个自动递增的id列,大多数Rails表都会这样做。

答案 1 :(得分:2)

您可以使用SQL,但您也可以使用

保持在activerecord土地上
@messages = Message.select('DISTINCT ON (foo) *').order(:created_at).reverse_order

DISTINCT ON ()语法是postgres。在MYSQL中有类似但略有不同的语法。

我认为这个查询最终会比接受的答案更有效率,因为它只是一种排序和扫描,而不是N种类。

答案 2 :(得分:1)

使用更专用的子查询完成所有工作,没有GROUP BY调用:

SELECT *
   FROM `messages`
   WHERE `id` = (
      SELECT `id`
      FROM `messages` as `alt`
      WHERE `alt`.`foo` = `messages`.`foo`
      ORDER BY `created_at` DESC
      LIMIT 1
   )
   ORDER BY `created_at` DESC

感谢Chaos在这个帖子中的回答:Select the 3 most recent records where the values of one column are distinct

但是,不知道任何速度影响。如果有人想介入,请随意这样做。

答案 3 :(得分:1)

我刚遇到同样的问题。以下Rails查询对我有用:

Message.where("id IN (SELECT MAX(id) FROM messages GROUP BY id) AND state = 'unread'")

在这里,我们使用子查询来获取每个组中最大的(因此也是最近的)ID,然后将这些ID过滤为仅显示state ==' unread'。

的那些ID。

提示:我在Message模型类中创建了一个self.latest方法,该方法包含:

def self.latest
  where("id IN (SELECT MAX(id) FROM messages GROUP BY id)
end

这意味着我可以在这样的控制器中使用它:

Message.latest.where(state: 'unread')

答案 4 :(得分:0)

我没有看到如何使用Arel轻松完成这项工作 - 但如果您只是像在问题中那样构建自己的SQL,那么请按如下方式修改它:

@messages = Message.find_by_sql("
  SELECT messages.* 
  FROM messages 
  GROUP BY messages.foo 
  HAVING messages.created_at = (SELECT max(created_at) FROM messages AS sub_messages WHERE sub_messages.foo = messages.foo)

请注意HAVING子句中的子查询。您的HAVING子句计算了所有消息中的MAX - 而不是foo中的消息。子查询应该解决这个问题。

我将消息表名称别名为sub_messages - 但这不是必需的。我只是为了“显示”父和子查询如何协同工作。