【问题标题】:Difference between size, length and count in complicated ActiveRecord case复杂 ActiveRecord 案例中大小、长度和计数之间的差异
【发布时间】:2012-08-10 16:04:54
【问题描述】:
[10] pry(main)> r.respondents.select(:name).uniq.size

(1.1ms)  SELECT DISTINCT COUNT("respondents"."name") FROM "respondents" 
INNER JOIN "values" ON "respondents"."id" = "values"."respondent_id" WHERE 
"values"."round_id" = 37 => 495

[11] pry(main)> r.respondents.select(:name).uniq.length

Respondent Load (1.1ms)  SELECT DISTINCT name FROM "respondents" 
INNER JOIN "values" ON "respondents"."id" = "values"."respondent_id" WHERE
"values"."round_id" = 37 => 6

为什么每个查询返回的结果不同?

【问题讨论】:

    标签: sql ruby-on-rails activerecord


    【解决方案1】:
    .count #=> this always triggers a SELECT COUNT(*) on the database
    
    .size #=> if the collection has been loaded, defers to Enumerable#size, else does the SELECT COUNT(*)
    
    .length #=> always loads the collection and then defers to Enumerable#size
    

    【讨论】:

      【解决方案2】:

      r.respondents.select(:name).uniq 返回一个 ActiveRecord::Relation 对象,该对象覆盖size

      见:http://api.rubyonrails.org/classes/ActiveRecord/Relation.html#method-i-size

      对此类对象调用size 会检查该对象是否“已加载”。

      # Returns size of the records.
      def size
        loaded? ? @records.length : count
      end
      

      如果是“已加载”,则返回@records 数组的长度。否则,它会调用count,如果没有参数,它将“return a count of all the rows for the model.

      那么为什么会有这种行为呢? AR::Relation 只有在首先调用 to_aexplain 时才会“加载”:

      https://github.com/rails/rails/blob/master/activerecord/lib/active_record/relation.rb

      load 方法上方的评论解释了原因:

      # Causes the records to be loaded from the database if they have not
      # been loaded already. You can use this if for some reason you need
      # to explicitly load some records before actually using them. The
      # return value is the relation itself, not the records.
      #
      #   Post.where(published: true).load # => #<ActiveRecord::Relation>
      def load
        unless loaded?
          # We monitor here the entire execution rather than individual SELECTs
          # because from the point of view of the user fetching the records of a
          # relation is a single unit of work. You want to know if this call takes
          # too long, not if the individual queries take too long.
          #
          # It could be the case that none of the queries involved surpass the
          # threshold, and at the same time the sum of them all does. The user
          # should get a query plan logged in that case.
          logging_query_plan { exec_queries }
        end
      
        self
      end
      

      因此,也许使用AR::Relation#size 来衡量此关系上查询的潜在复杂性的大小,其中length 回退到返回记录的计数。

      【讨论】:

        【解决方案3】:

        在将 Rails 3.2 转换为 4.1 时,似乎 AR::Relation#size 有所不同。以前它返回“行”的数量,而(在我的情况下)它现在返回一个哈希。更改为使用 #count 似乎与 3.2 中的 #size 给出相同的结果。我在这里有点含糊,因为在 4.1 上的“rails 控制台”中运行测试在 4.1 上通过“rails 服务器”运行时并没有给出相同的结果

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-02-15
          • 1970-01-01
          • 1970-01-01
          • 2021-10-07
          • 2018-03-09
          相关资源
          最近更新 更多