【问题标题】:What is the best way to order by parent and child's created_at?父母和孩子的 created_at 订购的最佳方式是什么?
【发布时间】:2014-01-12 00:52:19
【问题描述】:

假设我有一个有很多回复的父评论。

class Comment < ActiveRecord::Base
    has_many :replies

class Reply < ActiveRecord::Base
    belongs_to :topic

我想通过created_at 订购我的评论,但是如果他们有更新的回复,我希望父评论按其最新回复的created_at 排序,而不是它的自己created_at。

因此,例如,如果我有 3 条评论,一条针对第 1 天、第 2 天和第 3 天发布。第 1 天是最旧的,但第 2 天有一条今天发布的回复,我该如何对其进行排序,以便结果是:

[ Day 2, Day 3, Day 1 ]

第 2 天有最近的活动,比当天早些时候发布的第 3 天评论更新。

对此有什么方法?

【问题讨论】:

  • 我很确定这是“向我们展示你迄今为止的工作”类型的反对票。我冒昧地改写了你问题的最后一句话,所以看起来你不是在要求别人编写你自己的代码。

标签: ruby-on-rails postgresql activerecord


【解决方案1】:

最好通过模型来处理日期:

class Comment < ActiveRecord::Base
  has_many :replies
end

class Reply < ActiveRecord::Base
  belongs_to :comment, touch: true
end

注意 belongs_to: comment 关联上的 touch: true。这将在保存 Reply 时更新父 Commentupdated_at 属性

现在只需在选择您的 cmets 时通过 updated_at 订购。

【讨论】:

  • 请注意,这种方法意味着每个回复都会锁定正在回复的评论,直到事务提交。因此,您一次最多可以有一个 tx 影响任何给定的评论。这可能没问题,但在采取这种方法时也应该考虑。锁定父母的子更新也会暴露死锁风险。
  • 我认为在这种情况下,锁定几乎不会引起注意。如果保存回复记录需要花费大量时间,那么就大错特错了。此外,对评论行的任何进一步更新只会在很短的时间内排队,而不是完全阻止。
  • 当然,服务员只会阻塞直到持有 tx 的锁提交。当您在树上遵循这种策略到争用资源时,这真的只是一个问题,您会在一个父对象上获得大量活动等。
【解决方案2】:

对此的最佳查询(如性能最佳的查询)是添加一个额外的last_activity 列。维护后者(自动,使用触发器或一些 RoR 内置糖)并在其上添加索引。然后,您将能够使用普通查询获取按该列排序的行。

另一种选择是与聚合的丑陋连接(请参阅一小时前的答案)。它不会很漂亮,而且随着您的桌子变大,它的性能会非常糟糕

【讨论】:

    【解决方案3】:

    我想这样的事情可能是“好的”,但请查看查询计划。 (我自己只处理 LINQ 和 SQL Server - YMMV。)

    select * from comments c 
    left join (select r.comment_id, max(r.created_at) as created_at
               from replies r
               group by r.comment_id) lr
    on lr.comment_id = c.comment_id
    order by isnull(lr.created_at, c.created_at) desc
    

    【讨论】:

      【解决方案4】:

      LEFT JOIN 到最新响应(可能不存在,因此LEFT)之后,在ORDER BY 中使用(SQL 标准)COALESCE。但根据您的要求,只有comments 中的SELECT 列:

      SELECT c.*
      FROM   comments c 
      LEFT   JOIN  (
         SELECT comment_id, max(created_at) AS created_at
         FROM   replies
         GROUP  BY 1
        ) r USING (comment_id)
      ORDER  BY COALESCE(r.created_at, c.created_at) DESC
      

      由于逻辑规定回复必须在 cmets 之后。如果不是这样,您可以改用GREATEST(r.created_at, c.created_at)

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2013-01-19
        • 2020-01-24
        • 2012-06-24
        • 1970-01-01
        • 2020-09-07
        • 1970-01-01
        • 2014-05-28
        相关资源
        最近更新 更多