Qqw*_*qwy 12 ruby mysql activerecord ruby-on-rails ruby-on-rails-4
这是我经常遇到的问题.关于这个问题有一些类似的问题,但是没有一个是非常完整的(并且它们可能已经过时了,因为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)
Run Code Online (Sandbox Code Playgroud)
缺点:为每个用户创建一个DataBase请求,使得此解决方案非常慢.
2)直接使用SQL代码加入(参见例如这个问题和答案)
select('users.*, COUNT(posts.id) AS posts_count').joins(:posts).group('users.id').order('posts_count DESC').take(5)
Run Code Online (Sandbox Code Playgroud)
这将运行DataBase中的所有排序逻辑.然而:
3)直接使用SQL与外部联接(例如,请参阅此问题和答案)
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")
Run Code Online (Sandbox Code Playgroud)
这也会返回没有帖子的用户.缺点:
4)使用计数器缓存列(有关此技术的完整说明,请参阅此Railscasts剧集)
基本上,创建一个新列,通过在每次创建或删除新帖子时更改字段中的值来User跟踪posts该用户的当前计数.
这非常快速且可读.缺点是我们只能在我们定义了一个新字段后使用它User.对于许多情况,这是可以接受的,但是更难以灵活,因为需要更改用户表,以便按照我们可能想要创建前五的关联工作.此外,由于这是一个缓存字段,因此存在不会触发字段更新的数据库操作.
有没有更好(可读和有效)的方法来实现这一目标?优选使用内置ActiveRecord方法的东西.
另一种方法,有一些限制可能使它更像是一个部分解决方案:
User.where(:id => Post.group(:user_id).
order("count(*) desc").
limit(5).
keys)
Run Code Online (Sandbox Code Playgroud)
在查找具有最多帖子数量的五个用户时,这在数据库术语中非常有效,因为它只需要扫描posts表的user_id列上的索引,因此对于非常大的数据集会有好处.它也是非常"干净"的Rails/ActiveRecord代码,应该与数据库无关.
如果以后计数顺序返回用户是关键的,那么一旦识别出这五个,就可以使用效率较低的排序方法,或者可以在ruby中使用密钥的检索顺序来对返回的用户进行排序.
这是一个值得一看的方法:
User.joins("left join posts on posts.user_id = users.id").
group(:id).
order("count(*) desc").
limit(5)
Run Code Online (Sandbox Code Playgroud)
在连接中需要一点手动操作,但是如果您知道至少有五个用户有帖子,或者不想列出任何没有帖子的用户,那么您可以使用常规连接:
User.joins(:posts).
group(:id).
order("count(*) desc").
limit(5)
Run Code Online (Sandbox Code Playgroud)
如果您有其他 has_many 连接,则 count(*) 不一定可靠,但在这种情况下,您可能希望生成一个查询,例如:
select ...
from users ...
order by (select count(*) from posts where posts.user_id = users.id)
Run Code Online (Sandbox Code Playgroud)
ps 在 PostgreSQL 上测试。ID 列上的 GROUP BY 在 Oracle 上肯定不起作用,不确定其他的。
| 归档时间: |
|
| 查看次数: |
2918 次 |
| 最近记录: |