Rails 3:.group() 按created_at 排序

发布于 2024-12-13 20:36:50 字数 709 浏览 3 评论 0原文

Rails 3 在 Activerecord 中排序 .group() 结果的方式是什么(此处为“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 值

What is the Rails 3 way to order .group() results in Activerecord (here by "created_at")?

@messages = Message.group(:foo)

only results in displaying the oldest message. I'd need the latest to be shown.

I tried

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

with no success. Any hints appreciated!

To clarify: I'm looking to have the group ordered within itself, not a normal messages.order("...").
Should there be no easy Activerecord syntax, i'd be happy with raw SQL as well


Update: trying the SQL way, this was supposed to work:

@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")

But this retrieves solely single records (those that are not grouped). Supposedly grouped ones are being omitted. Do not know why, all records have :created_at and :foo values

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

悟红尘 2024-12-20 20:36:50

在某些数据库下,您无法使用 order 然后在同一查询中进行分组,因此您需要使用两个查询来完成此操作:

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

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

You can't use order then group in the same query under some databases, so you need to do it using two queries:

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

Note: this assumes that you have an auto-incremented id column, which most Rails tables do.

拧巴小姐 2024-12-20 20:36:50

你可以使用 SQL,但你也可以大部分时间停留在 activerecord 领域,使用

@messages = Message.select('DISTINCT ON (foo) *').order(:created_at).reverse_order

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

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

You can go SQL, but you can also mostly stay in activerecord land with

@messages = Message.select('DISTINCT ON (foo) *').order(:created_at).reverse_order

The DISTINCT ON () syntax is postgres. There's a similar but slightly different syntax in MYSQL.

I think this query will end up being a lot more efficient, too, than the accepted answer because it's just a single sort and scan, instead of N sorts.

浪推晚风 2024-12-20 20:36:50

通过使用更专用的子查询来实现一切,无需 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 在该线程中的回答: 选择其中一列的值不同的 3 个最新记录

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

Got it all working by using a more dedicated subquery, without a GROUP BY call:

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

All thanks to Chaos' answer in this thread: Select the 3 most recent records where the values of one column are distinct

Don't know about any speed implications yet though. If anyone wants to step in, please feel free to do so.

孤独陪着我 2024-12-20 20:36:50

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

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

这里我们使用子查询来获取每个组中最大的(因此也是最新的)ID,然后过滤这些 ID 以仅显示状态 == '未读' 的 ID。

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

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

这意味着我可以在控制器中使用它,如下所示:

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

I have just come across this same problem. The following Rails query worked for me:

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

Here we use a sub-query to get the largest (and therefore most recent) ID in each group, and then filter those to only show ones where state == 'unread'.

Tip: I made a self.latest method in my Message model class that consisted of:

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

Which meant I could use it in controllers like this:

Message.latest.where(state: 'unread')
浊酒尽余欢 2024-12-20 20:36:50

我不明白如何使用 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 - 但这不是必需的。我这样做只是为了“展示”父查询和子查询如何协同工作。

I don't see how this would be done using Arel easily - but if you just go with constructing your own SQL like you did in your question, then amend it as follows:

@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)

Note the subquery in the HAVING clause. Your HAVING clause calculated the MAX across all your messages - as opposed to only the messages within foo. The subquery should solve that problem.

I aliased the messages table name to sub_messages - but that shouldn't be necessary. I just did it to 'show' how the parent and subquery work together.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文