mysql - 根据 has_many 关系按计数排序

标签 mysql ruby-on-rails ruby ruby-on-rails-4 activerecord

这是我经常遇到的问题。关于这个问题有一些类似的问题,但没有一个是非常完整的(而且它们可能已经过时,因为 Rails 4 可能引入了有助于解决这个问题的新功能)

让我举一个简单的问题示例和“解决”问题的已知方法:


假设我有一个 User 模型和一个 Post 模型,以及一个 User has_many :posts

现在,我想获得帖子最多的前五名用户。

以下是我知道的选项,但它们都有各自的缺点:

1)

users = User.all
@top_users = users.sort {|a,b| a.posts.count <=> b.posts.count}.take(5)

缺点:为每个用户发出一个数据库请求,使该解决方案非常慢。

2) 直接使用带有 Join 的 SQL 代码 (例如参见 this question and answer )

select('users.*, COUNT(posts.id) AS posts_count').joins(:posts).group('users.id').order('posts_count DESC').take(5)

这将运行数据库中的所有排序逻辑。然而:

  • 我们使用了大量特定于数据库的代码(例如,在 PostgreSQL 中,我们需要其他语法)。如果可能,最好使用 ActiveRecord 方法。
  • 使用内部加入意味着永远不会返回没有任何帖子的用户。当我们还想返回没有帖子的用户时,这就是一个问题。

3) 直接将 SQL 与外连接一起使用(参见实例 this question and answers)

User.select("users.*, COUNT(posts.id) as posts_count").joins("LEFT OUTER JOIN posts ON posts.user_id = users.id").group("posts.id").order("posts_count DESC")

这也会返回没有帖子的用户。缺点:

  • 更多特定于数据库的代码如#2,甚至更难阅读。

4) 使用计数器缓存列 (有关此技术的完整说明,请参阅 this Railscasts episode )

基本上,在 User 上创建一个新列,通过每次发布新帖子时更改字段中的值来跟踪该用户的当前 posts 计数被创建或删除。

这是非常快速和可读的。缺点是只有在 User 上定义了一个新字段后,我们才能使用它。在许多情况下,这是可以接受的,但要变得灵活会更难,因为需要更改用户表才能使其在每个我们可能希望为其创建前五名的关联中工作。此外,由于这是一个缓存字段,因此存在不会触发该字段更新的数据库操作。

是否有更好(可读且高效)的方法来完成此任务?最好使用内置的 ActiveRecord 方法。

最佳答案

另一种方法,有一些限制可能使其更像是部分解决方案:

User.where(:id => Post.group(:user_id).
                       order("count(*) desc").
                       limit(5).
                       keys)

从数据库的角度来看,这在查找帖子数量最多的五个用户方面非常有效,因为它只需要扫描帖子表的 user_id 列上的索引,因此对于非常大的数据集非常有用。它也是相当“干净”的 Rails/ActiveRecord 代码,实际上应该独立于数据库。

如果按照统计后的顺序返回用户很关键,那么一旦这五个被识别出来,就可以使用效率较低的排序方法,或者可以在 ruby​​ 中使用键的检索顺序对返回的用户进行排序。

关于mysql - 根据 has_many 关系按计数排序,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31918029/

相关文章:

ruby-on-rails - Rails 3 中的断言差异

ruby-on-rails - 为什么我不能在 Rails 中使用 Record.all.destroy?

MySQL 左连接 : if primary condition does not exist use backup condition return only one

php - 在 foreach 循环中调用存储过程 - 仅首先执行

php - CakePHP shell cronjobs 在更改密码后停止工作

ruby-on-rails - Rails - 全局化和 Permanent_record 依赖 : :destroy callbacks

ruby-on-rails - 被 Rails 中的时区困扰

mysql - 计算每个 ID 的 unix 时间戳之间的行数

ruby-on-rails - 如何使方法出现在所有 Controller 操作之前

ruby - 如何抓取 HTML 中没有样式的文本?