【问题标题】:ActiveRecord: size vs countActiveRecord:大小与计数
【发布时间】:2011-08-30 07:20:25
【问题描述】:

在 Rails 中,您可以同时使用 Model.sizeModel.count 来查找记录数。如果您正在处理更复杂的查询,使用一种方法比另一种方法有什么优势吗?它们有何不同?

例如,我的用户有照片。如果我想显示一个用户表以及他们有多少张照片,运行多个user.photos.size 实例会比user.photos.count 快​​还是慢?

谢谢!

【问题讨论】:

    标签: ruby-on-rails ruby ruby-on-rails-3 rails-activerecord


    【解决方案1】:

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

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

    基本上:

    • 如果你已经加载了所有条目,比如User.all,那么你应该使用length来避免另一个数据库查询

    • 如果您没有加载任何内容,请使用count 对您的数据库进行计数查询

    • 如果您不想考虑这些问题,请使用size,它会适应

    【讨论】:

    • 如果size无论如何都适应了这种情况,那么lengthcount还需要什么?
    • @sscirus - 这样size 可以在您拨打size 时拨打他们的电话(在它确定要拨打哪个电话之后)。
    • 但是,请注意仅默认为大小。例如,如果您在不经过关系的情况下创建新记录,即Comment.create(post_id: post.id),您的post.comments.size 将不会是最新的,而post.comments.count 会。所以要小心。
    • 另外,如果您通过关系构建多个对象:company.devices.build(:name => "device1"); company.devices.build(:name => "device2"),那么company.devices.size.length 将包括您已构建但尚未保存的对象数量,.count将仅报告数据库中的计数。
    • @sscirrus,size 是一个危险的命令,因为它是自动化的,有时您确实想再次查询数据库。
    【解决方案2】:

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

    Model.count
    
    Model.all.size
    
    records = Model.all
    records.count
    

    下面的效率不如它将所有记录从数据库加载到 Ruby 中,然后计算集合的大小。

    records = Model.all
    records.size
    

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

    【讨论】:

    • Model.all.sizeModel.all.count 在 Rails 4 及更高版本中生成 count 查询。 size 的真正优势在于,如果关联已加载,它不会生成计数查询。在 Rails 3 及更低版本中,我相信 Model.all 不是关系,因此所有记录都已加载。这个答案可能已经过时了,我建议删除它。
    【解决方案3】:

    正如其他答案所说:

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

    但还有一件事。我们注意到sizecount/lengthaltogether 的行为不同的情况,我想我会分享它,因为它很少被忽视。

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

      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
      

    the Rails Guides 中记录了这种行为,但我不是第一次错过了它,就是忘记了它。

    【讨论】:

    【解决方案4】:

    有时size“选错了”并返回一个散列(这是count会做的)

    在这种情况下,请使用 length 获取 整数 而不是 散列

    【讨论】:

    • 我在来自 has_many 实例的集合上使用了“.size”,即使集合中有一条记录,size 也会返回“0”。使用 .count 返回正确的值 '1'。
    【解决方案5】:

    我推荐使用 size 函数。

    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 个客户活动,我尝试使用 count、length 和 size 方法分别统计该客户的客户活动记录数。下面是所有这些方法的基准报告。

                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 是计算记录数的最佳选择。

    【讨论】:

      【解决方案6】:

      tl;博士

      • 如果您知道自己不需要这些数据,请使用 count
      • 如果您知道您将使用或已经使用数据,请使用length
      • 如果你不知道用在哪里或者速度差异可以忽略,使用size...

      计数

      解决向数据库发送Select count(*)... 查询。如果您不需要数据,而只需要计数,那么该走的路。

      示例:新消息的计数、仅显示页面时的总元素等。

      长度

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

      示例:满载表格的摘要、显示数据的标题等

      尺寸

      它检查数据是否已加载(即已经在 rails 中),如果是,则只计算它,否则它调用 count。 (加上其他条目中已经提到的陷阱)。

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

      有什么问题?

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

      【讨论】:

      • “如果你不知道自己在做什么”?
      • @SebastianPalma 太苛刻了?好的,改写得更准确(和礼貌:)
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-11-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多