ActiveRecord:大小与计数


201

在Rails中,您可以使用Model.size和来查找记录数Model.count。如果您要处理更复杂的查询,使用一种方法相对于另一种方法有什么好处吗?它们有何不同?

例如,我的用户有照片。如果我想显示一张用户表以及他们拥有多少张照片,那么运行多个实例的user.photos.size速度会比更快或更慢user.photos.count

谢谢!

Answers:


344

您应该阅读该内容,它仍然有效。

您将根据需要调整使用的功能。

基本上:

  • 如果您已经加载了所有条目,例如User.all,则应该使用length以避免另一个数据库查询

  • 如果尚未加载任何内容,请使用countdb进行计数查询

  • 如果您不想打扰这些注意事项,请使用size适合您的


35
如果size适应反正这种情况,那么什么需要的是有没有lengthcount呢?
sscirrus'5

27
@sscirus-这样,size您可以在拨打电话时size(在确定要拨打的电话后)与他们通话。
巴特金斯2011年

35
但是,请注意默认大小。例如,如果您创建新记录而不经历关系,即Comment.create(post_id: post.id),您post.comments.size将不会保持最新,而post.comments.count会保持最新。因此,请小心。
mrbrdo

14
另外,如果您通过一个关联关系构建多个对象:company.devices.build(:name => "device1"); company.devices.build(:name => "device2"),然后company.devices.size并且.length将包括已构建但尚未保存的对象数,.count则将仅报告数据库中的计数。
肖恩·高夫

6
@ sscirrus,size是一个危险的命令,因为它是自动执行的,有时您确实想再次查询数据库。
Alex C

79

其他答案指出:

  • count将执行一个SQL COUNT查询
  • length 将计算结果数组的长度
  • size 将尝试从两者中选择最合适的一个,以避免过多的查询

但是还有一件事。我们注意到一个案例的size行为与count/ length完全不同,我想分享一下,因为这种情况很少被忽略。

  • 如果您:counter_cachehas_many关联上size使用,将直接使用缓存的计数,而根本不会进行额外的查询。

    class Image < ActiveRecord::Base
      belongs_to :product, counter_cache: true
    end
    
    class Product < ActiveRecord::Base
      has_many :images
    end
    
    > product = Product.first  # query, load product into memory
    > product.images.size      # no query, reads the :images_count column
    > product.images.count     # query, SQL COUNT
    > product.images.length    # query, loads images into memory

这种行为已在Rails指南中进行了记录,但是我还是第一次错过了它,或者忘记了它。


实际上,在rails 5.0.0.beta1之前,即使存在_count列(counter_cache: true关联上没有指令),也会触发此行为。这已被固定在github.com/rails/rails/commit/e0cb21f5f7
cbliard

8

有时size“选择错误的对象”并返回哈希count可以这样做)

在这种情况下,可使用length获取整数而不是hash


我在has_many实例的集合上使用了“ .size”,即使集合中有一条记录,size仍返回“ 0”。使用.count返回正确的值'1'。
admazzola

4

tl; dr

  • 如果您知道不需要使用数据count
  • 如果您知道您将使用或已经使用了数据用途length
  • 如果您不知道自己在做什么,请使用size...

计数

解决将Select count(*)...查询发送到数据库的问题。如果您不需要数据,而只需要计数,该怎么办。

例如:新消息的计数,仅将要显示页面时的元素总数等。

长度

加载所需的数据,即根据需要查询,然后对其进行计数。使用数据时的处理方式。

示例:满载表的摘要,显示数据的标题等。

尺寸

它检查是否已加载数据(即已加载到导轨中),然后对其进行计数,否则调用count。(加上其他条目已经提到的陷阱)。

def size
  loaded? ? @records.length : count(:all)
end

有什么问题?

如果您未按正确的顺序进行操作,您可能会两次访问数据库(例如,如果在已渲染的表顶部渲染表中的元素数量,则实际上将有2个调用发送到数据库)。


3

以下策略均调用数据库以执行COUNT(*)查询。

Model.count

Model.all.size

records = Model.all
records.count

以下方法效率不高,因为它将所有记录从数据库加载到Ruby中,然后Ruby计算集合的大小。

records = Model.all
records.size

如果您的模型具有关联,并且您想查找所属对象的数量(例如@customer.orders.size),则可以避免数据库查询(磁盘读取)。使用计数器缓存,Rails将使缓存值保持最新,并根据该size方法返回该值。


2
二者Model.all.sizeModel.all.count产生一个count在轨道4和上面的查询。真正的优势size在于,它不产生计数查询,如果该协会已经加载。在Rails 3和更低版本中,我相信Model.all这不是一个关系,因此所有记录都已加载。该答案可能已过期,建议删除它。
达蒙·奥

1

我建议使用尺寸功能。

class Customer < ActiveRecord::Base
  has_many :customer_activities
end

class CustomerActivity < ActiveRecord::Base
  belongs_to :customer, counter_cache: true
end

考虑这两个模型。客户有许多客户活动。

如果在has_many关联上使用:counter_cache,则size将直接使用缓存的计数,而根本不会进行额外的查询。

考虑一个示例:在我的数据库中,一个客户有20,000个客户活动,我尝试使用计数,长度和大小方法分别计算该客户的客户活动记录数。下面是所有这些方法的基准报告。

            user     system      total        real
Count:     0.000000   0.000000   0.000000 (  0.006105)
Size:      0.010000   0.000000   0.010000 (  0.003797)
Length:    0.030000   0.000000   0.030000 (  0.026481)

所以我发现使用:counter_cache Size是计算记录数的最佳选择。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.