【问题标题】:ActiveRecord return the newest record per user (unique)ActiveRecord 返回每个用户的最新记录(唯一)
【发布时间】:2014-11-05 19:39:04
【问题描述】:

我有一个用户模型和一个卡片模型。用户有很多卡片,所以卡片有一个属性user_id

我想为每个用户获取最新的单张卡片。我已经能够做到这一点:

Card.all.order(:user_id, :created_at)
# => gives me all the Cards, sorted by user_id then by created_at

这让我走到了一半,我当然可以遍历这些行并抓住每个用户的第一个。但这对我来说真的很糟糕,因为我会在 Ruby 中使用数组来做很多这样的事情。

我也可以这样做:

Card.select('user_id, max(created_at)').group('user_id')
# => gives me user_id and created_at

...但我只取回 user_ids 和 created_at 时间戳。我无法选择任何其他列(包括 id),所以我得到的东西毫无价值。我也不明白为什么 PG 不允许我选择比上面更多的列而不将它们放入 group_by 或聚合函数中。

我更愿意找到一种仅使用 ActiveRecord 来获得我想要的东西的方法。我也愿意用原始 SQL 编写这个查询,但前提是我不能用 AR 完成它。顺便说一句,我使用的是 Postgres DB,这限制了我的一些选择。

谢谢大家。

【问题讨论】:

  • Card.all.order(:user_id, :created_at).firstCard.all.order(:user_id).order(:created_at).first 怎么样
  • @Bala 我希望为所有制作卡片的用户每个用户提供最新的卡片。这只会给我最低 ID 且至少有一张卡的用户的最新卡。
  • 哪一个给了你最低的ID?只需将:desc 添加到:created_at
  • 我很确定这是 AR ORM 功能不足的情况之一,您需要转至 find_by_sql。有关所需的 SQL,请参阅 stackoverflow.com/questions/1313120/… 及其副本。我遇到了一个相关的问题,最终需要维护一个“最后一条记录”ID 表以使查询足够快(尽管我需要最后一个 K,而不是最后一个 1)。您可以在链接的讨论中看到,对于一百万条记录,查询需要几分钟时间。
  • @Bala 这仍然只会返回我一条记录。如果我有 10 个用户,并且每个用户至少制作了 1 张卡,我想为每个用户获取最新的卡。所以查询应该返回 10 张卡片,而不是 1 张。

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


【解决方案1】:

我们自己加入卡片表,ON

a) first.id != second.id
b) first.user_id = second.user_id
c)first.created_at < second.created_at

Card.joins("LEFT JOIN cards AS c ON cards.id != c.id AND c.user_id = cards.user_id AND cards.created_at < c.created_at").where('c.id IS NULL')

【讨论】:

  • 收到此错误:PG::DatatypeMismatch: ERROR: argument of HAVING must be type boolean, not type timestamp without time zone LINE 1: ...T "cards".* FROM "cards" GROUP BY user_id HAVING MAX(create...
  • 其实我自己测试过,运行没有错误,我用另一种方式更新了解决方案
  • 不适用于 Postgres。第一个命令给我一个PG::DatatypeMismatch: ERROR:,第二个给我PG::GroupingError: ERROR:
  • 没有给我每个用户一张卡
【解决方案2】:

这有点晚了,但我正在做同样的事情,我发现这个对我有用:

Card.all.group_by(&:user_id).map{|s| s.last.last}

你怎么看?

【讨论】:

    【解决方案3】:

    我发现一种解决方案在性能方面并不理想,但适用于非常小的数据集,当时间​​很短或这是一个爱好项目时:

    Card.all.order(:user_id, :created_at).to_a.uniq(&:user_id)
    

    这会获取 AR:Relation 结果,将它们转换为 Ruby 数组,然后使用 Proc 对结果执行 Array#uniq。经过一些简短的测试后,#uniq 似乎会保持秩序,所以只要在使用 uniq 之前一切都井井有条,你应该是好的。

    该功能对时间敏感,所以我现在将使用它,但我将在@Gene 的响应和链接之后查看原始 SQL 中的某些内容。

    【讨论】:

    • 不知道为什么这被低估了性能。有人有什么想法吗?
    • 可能是性能,并使其在结果中浮到底部?我刚刚测试了你上面的答案,它确实有效。您收到的错误可能来自您使用的数据类型的问题。我只是在另一个答案中使用了代码,效果很好。
    • @gregb 这是因为来这里寻找答案以通过 AR 让 SQL 完成繁重工作的人们对问题标题误导了“选择的答案”感到失望
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-10-04
    • 1970-01-01
    • 2018-07-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多