Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/ruby-on-rails/66.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Mysql 按计数排序有许多关系_Mysql_Ruby On Rails_Ruby_Ruby On Rails 4_Activerecord - Fatal编程技术网

Mysql 按计数排序有许多关系

Mysql 按计数排序有许多关系,mysql,ruby-on-rails,ruby,ruby-on-rails-4,activerecord,Mysql,Ruby On Rails,Ruby,Ruby On Rails 4,Activerecord,这是我经常遇到的一个问题。关于这个问题也有一些类似的问题,但没有一个是非常完整的(而且它们可能已经过时了,因为Rails 4可能已经引入了帮助解决这个问题的新功能) 让我举一个简单的例子来说明这个问题以及“解决”这个问题的已知方法: 假设我有一个User模型和一个Post模型,一个用户有很多:posts 现在,我想在帖子最多的用户中排名前五 以下是我知道的选项,但它们都有自己的缺点: (一) 这将运行数据库中的所有排序逻辑。然而: 我们使用了大量特定于DB的代码(例如,在PostgreSQL

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

让我举一个简单的例子来说明这个问题以及“解决”这个问题的已知方法:


假设我有一个
User
模型和一个
Post
模型,一个
用户有很多:posts

现在,我想在帖子最多的用户中排名前五

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

(一)

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

  • 我们使用了大量特定于DB的代码(例如,在PostgreSQL中,我们需要其他语法)。如果可能的话,最好使用ActiveRecord方法
  • 使用内部连接意味着没有任何帖子的用户将永远不会被返回。当我们想要返回没有帖子的用户时,这也是一个问题
3) 直接将SQL与外部联接一起使用(例如,请参见)

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

  • 更具体的DB代码为#2,更难阅读
4) 使用计数器缓存列 (有关此技术的完整说明,请参阅)

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

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


有没有更好的(可读性和效率)方法来实现这一点?最好是使用内置的ActiveRecord方法。

这里有一个方法值得一看:

User.joins("left join posts on posts.user_id = users.id").
     group(:id).
     order("count(*) desc").
     limit(5)
在加入中有点手动,但是如果您知道至少有五个用户有帖子,或者不想列出任何没有帖子的用户,那么您可以使用常规加入:

User.joins(:posts).
     group(:id).
     order("count(*) desc").
     limit(5)
如果有其他连接,则计数(*)不一定可靠,但在这种情况下,您可能希望生成一个查询,例如:

select ...
from   users ...
order by (select count(*) from posts where posts.user_id = users.id)

p、 在PostgreSQL上测试。ID列上的GROUP BY肯定无法在Oracle上工作,对其他选项也不确定。

此选项可能值得研究,但没有对其进行测试,因此可能需要进行一些调整

class Post < ActiveRecord::Base
  belongs_to :user, counter_cache: true
end
在用户表中添加默认为0的
posts\u count
integer列

class AddPostsCountToUsers < ActiveRecord::Migration
  def change
    add_column :users, :posts_count, :integer, default: 0
  end
end

你也可以像下面这样做-

User.joins(:posts).select('users.*, count(*) as posts_count').group('users.id').order('posts_count')

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

User.where(:id => Post.group(:user_id).
                       order("count(*) desc").
                       limit(5).
                       keys)
从数据库的角度来看,这将非常有效地查找具有最多帖子数量的五个用户,因为它只需要扫描posts表的user_id列上的索引,因此对于非常大的数据集来说是很好的。它也是相当“干净”的Rails/ActiveRecord代码,实际上应该是独立于数据库的


如果按事后计数顺序返回用户是至关重要的,那么一旦确定了这五个用户,就可以使用效率较低的排序方法,或者可以在ruby中使用键的检索顺序对返回的用户进行排序。

还有另一个选项可以添加到您的列表中,但有缺点。您可以将字段
posts\u count
添加到
User
模型。如果帖子的添加频率低于前五名用户的选择频率,这可能是一个很好的尝试。
例如,在PostgreSQL中,我们需要其他语法
——在示例#2中,DB具体是什么?而不是显式的
左外部联接
,您可以尝试,
包括(…)
,它隐式地执行
外部联接
。此外,选项2和3中的查询似乎足够通用,因此语法可能在您感兴趣的SQL实现中很常见。@Glupo:Ah,是的。一个
缓存\u计数器
,对吗?我将把它添加到问题帖子中的可能性中。在许多情况下,这可能是一个很好的解决方案。但是,缺点当然是需要为此更改
用户
数据库表。这与方法#4不同吗?我非常喜欢这种方法!:-)
count(*)
对我来说是一个新概念,在这种情况下似乎非常聪明。它当然是有效的,但它与将任何条件应用于用户模型(如活动/非活动)以及使用多个订单条件(例如,按帖子数量排序,然后是评论数量排序)不兼容,所以不是每种情况都适用。为什么链中有一个计数呼叫?它是否只返回不适用于.keys的单个值?
class User < ActiveRecord::Base
  has_many :posts

  def self.top_5
    order('post_counts DESC').limit(5)
  end
end
class AddPostsCountToUsers < ActiveRecord::Migration
  def change
    add_column :users, :posts_count, :integer, default: 0
  end
end
User.find_each { |user| User.reset_counters(user.id, :posts) }
User.joins(:posts).select('users.*, count(*) as posts_count').group('users.id').order('posts_count')
User.where(:id => Post.group(:user_id).
                       order("count(*) desc").
                       limit(5).
                       keys)