简体   繁体   English

在 Rails 中进行计数的正确方法是什么?

[英]What's the right way to do counts in Rails?

I have a rails app with many following pieces of code:我有一个 Rails 应用程序,其中包含许多以下代码:

Our active community of <%= Account.find_all_by_admin(false).count %>

My question is is this the right way to do counts on views?我的问题是,这是对视图进行计数的正确方法吗? It seems so "dirty" is there a more railish, way to do counts?看起来如此“肮脏”有没有更荒谬的计数方法? I'm thinking named scopes perhaps, but I just want to be sure that these type of things won't have a greater impact in performance.我可能在考虑命名范围,但我只想确保这些类型的东西不会对性能产生更大的影响。

Thank You,谢谢你,

You don't need a name scope to perform a count.您不需要名称范围来执行计数。

Account.where(:admin => false).count

But named scopes are an excellent way to make your code more reusable.但是命名范围是使代码更可重用的极好方法。

Named scopes don't have any noticeable performance impact on your application.命名范围对您的应用程序没有任何明显的性能影响。

I would recommend you to avoid direct access to database in my templates because then you're losing a bit of flexibility when it comes to caching.我建议你避免在我的模板中直接访问数据库,因为那样你会在缓存方面失去一些灵活性。

Try to prepare all the data you need to render in your action instead and then use meaningful instance variables like @number_of_accounts or @accounts.count .尝试准备您需要在操作中呈现的所有数据,然后使用有意义的实例变量,例如@number_of_accounts@accounts.count

This will make your views cleaner and easier to debug and also a bit more DRY if you render action in different formats (html, json, etc)如果您以不同的格式(html、json 等)呈现动作,这将使您的视图更清晰、更易于调试,并且也会更 DRY

As to how do you get your numbers - it doesn't really matter that much, just move away from find_* methods towards scoping and write readable code至于你如何得到你的数字 - 它并不那么重要,只是从 find_* 方法转向范围和编写可读的代码

In rails 3 a simple call to count issues a simple count request:在 rails 3 中,一个简单的 count 调用会发出一个简单的 count 请求:

Contact.count

is resolved as:被解析为:

SELECT COUNT(*) AS count_id FROM "contacts"

a find all by field name will resolve as: find all by field name 将解析为:

Contact.find_all_by_country("Canada")

SELECT "contacts".* FROM "contacts" WHERE ("contacts"."country" = 'Canada')

I would recommend indexing your admin column for faster lookups and this can be translated into a named scope, but that by itself will only predefine the query, not optimize it.我建议为您的管理列建立索引以加快查找速度,这可以转换为命名范围,但它本身只会预定义查询,而不是优化它。

It is important to note that if you issue重要的是要注意,如果您发出

Contact.find_all_by_country("Canada").count

count is a method on the array class and doesn't actually issue a count on the database: count是数组类上的一个方法,实际上并不对数据库发出计数:

Contact.find_all_by_country("Canada").count

SELECT "contacts".* FROM "contacts" WHERE ("contacts"."country" = 'Canada')

A named scope shouldn't have an impact on performance命名范围不应影响性能

scope :not_admin, where(:admin => false)

Then you can have Account.not_admin.count然后你可以有Account.not_admin.count

Edited per DGM's comment: To check the generated SQL in a console, compare Account.not_admin.to_sql with Account.find_all_by_admin(false).to_sql根据 DGM 的评论进行编辑:要在控制台中检查生成的 SQL, Account.find_all_by_admin(false).to_sql Account.not_admin.to_sqlAccount.find_all_by_admin(false).to_sql进行比较

You can use following query instead of Account.where(:admin => false).count您可以使用以下查询代替Account.where(:admin => false).count

Account.select(:id).where(:admin => false).count

Just select one column, instead of selecting all.只需选择一列,而不是选择全部。 It generates the following query and it is faster than the previous one:它生成以下查询,并且比前一个查询更快:

SELECT COUNT("accounts"."id") FROM "accounts" where admin = false

You should create an instance variable in the controller and use it in the view.您应该在控制器中创建一个instance variable并在视图中使用它。 This will make you view clear.这会让你看得清楚。

@accounts_count = Account.where(admin: false).count

And use it in the view like `<%= @accounts_count %>并在视图中使用它,如 `<%= @accounts_count %>

If you want to loop though the results along with the count, you can use the following way:如果要循环遍历结果和计数,可以使用以下方式:

@accounts = Account.where(admin: false)

in view, write like below:鉴于,写如下:

<%= @accounts.count %>
// For looping account
<% @accounts.each do |account| %>
  # do some stuff
<% end %>

But the above way will fire 2 queries, 1 for count and other for the loop.但上述方式将触发 2 个查询,1 个用于计数,另一个用于循环。

Correct Approach正确的做法

Use size instead of count .使用size而不是count It will fire 1 query but you need to change the order of count in your view like:它将触发 1 个查询,但您需要更改视图中的计数顺序,例如:

// For looping account
<% @accounts.each do |account| %>
  # do some stuff
<% end %>
<%= @accounts.size %>

If you want the same order, then load the user object first like below如果你想要相同的顺序,那么首先加载用户对象,如下所示

<%= @accounts.load.size %>
// For looping account
<% @accounts.each do |account| %>
  # do some stuff
<% end %>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM