我们假设这个例子:
型号:
class User < ActiveRecord::Base
has_many :posts
end
控制器:
def index
@users = User.all
end
查看:
<ul>
<% @users.each do |u| %>
<li>
Username: <%= u.username %><br />
<%= pluralize(u.posts.count, "post") %>
</li>
<% end %>
</ul>
根据我的理解(通过在命令行中查看WEBrick),它不会为u.username
执行数据库调用,但为每个u.posts.count
执行在循环中循环。我想避免这种情况,所以我将“posts”存储在控制器中的实例变量中(例如@posts = Post.all
)并将u.posts.count
替换为@posts.where(:user_id => u.id).count
,但它仍然会执行每个周期的数据库调用。应用程序是否将所有信息都存储在控制器的@posts
数组中?
注意:这些都不是特定于我的情况(我没有显示用户列表);我只是用这个作为例子。
答案 0 :(得分:3)
处理此问题的一种方法是counter_cache
。您有效地向posts_count
模型添加了新的User
属性,并在每次修改用户帖子时更新它。 Rails使counter_cache
关联选项变得容易:
class User < ActiveRecord::Base
has_many :posts, counter_cache: true
end
使用该设置,调用u.posts.size
将返回存储在用户模型中的计数,而不是点击数据库(确保使用size
,而不是count
)。
有关:counter_cache
选项的详细信息,请查看Rails Association Basics guide (section 4.1.2.3)。 This blog post介绍了如何实际添加一个,包括迁移和初始化值。
你可以这样做的第二种方法是加载你控制器方法中的所有帖子,就像你想象的那样,但它可能比简单的加载它们更加简洁(@users = User.includes(:posts).all
)。它不适合您的原因是因为您使用count
而非size
- count
始终使用SELECT COUNT(*)
语句命中数据库,而size
更聪明,如果可能的话,将避免命中数据库。 (这就是为什么你在使用size
时也要确保使用counter_cache
。)
这也是有效的,但是如果你实际上不打算以任何方式使用这些帖子,最好避免将它们全部从数据库中删除,因此counter_cache
方法的吸引力。
答案 1 :(得分:0)
另一种选择是使用subselect进行SQL查询以进行计数。以下查询假定User.id = Post.user_id
@users = User.select("u.username").
select("count(p.id) AS post_count").
from("users u").
joins("LEFT JOIN posts p ON u.id = p.user_id").
group("u.id").
all
然后在你看来:
<% @users.each do |u| %>
<li>
Username: <%= u.username %><br />
<%= pluralize(u[:post_count], "post") %>
</li>
这应该是你输出到控制台:
Started GET "/reports" for 127.0.0.1 at 2013-11-28 16:33:31 -0700
Processing by ReportsController#index as HTML
User Load (0.8ms) SELECT u.username, count(p.id) AS post_total FROM users u LEFT JOIN posts p ON u.id = p.user_id GROUP BY u.id
Rendered users/index.html.erb within layouts/application (0.5ms)
Completed 200 OK in 10ms (Views: 7.3ms | ActiveRecord: 1.0ms)