count vs size vs length
区别在于是否重新发起select count,以及只发起select count还是select *
count
无论是否加载,count都会重新发起select count
单表时
[48] pry(main)> hus = Human.where('id < ?', 10);
[49] pry(main)> binding.trace_tree(htmp: 'rails/single_count'){hus.count}
(25.7ms) SELECT COUNT(*) FROM "humen" WHERE (id < 10)
=> 2
[50] pry(main)> hus
Human Load (0.1ms) SELECT "humen".* FROM "humen" WHERE (id < 10)
=> [#<human:0x007fbac86a3a08 id:="" 3,="" name:="" "jack",="" created_at:="" sun,="" 30="" jul="" 2017="" 15:14:01="" utc="" +00:00,="" updated_at:="" sun,="" 30="" jul="" 2017="" 15:14:01="" utc="" +00:00="">,
#<human:0x007fbac86a37b0 id:="" 6,="" name:="" "ken",="" created_at:="" tue,="" 01="" aug="" 2017="" 13:30:47="" utc="" +00:00,="" updated_at:="" tue,="" 01="" aug="" 2017="" 13:30:47="" utc="" +00:00="">]
[51] pry(main)> binding.trace_tree(htmp: 'rails/single_count_load'){hus.count}
(25.3ms) SELECT COUNT(*) FROM "humen" WHERE (id < 10)
=> 2</human:0x007fbac86a37b0></human:0x007fbac86a3a08>
未加载的调用栈
已加载的调用栈
关联时
[6] pry(main)> hu = Human.first
Human Load (0.1ms) SELECT "humen".* FROM "humen" ORDER BY "humen"."id" ASC LIMIT ? [["LIMIT", 1]]
=> #<human:0x007fbac7a210a0 id:="" 3,="" name:="" "jack",="" created_at:="" sun,="" 30="" jul="" 2017="" 15:14:01="" utc="" +00:00,="" updated_at:="" sun,="" 30="" jul="" 2017="" 15:14:01="" utc="" +00:00="">
[7] pry(main)> binding.trace_tree(htmp: 'rails/count'){hu.cells.count}
(33.7ms) SELECT COUNT(*) FROM "cells" WHERE "cells"."human_id" = ? [["human_id", 3]]
=> 2
[8] pry(main)> hu = Human.first;hu.cells
Human Load (0.1ms) SELECT "humen".* FROM "humen" ORDER BY "humen"."id" ASC LIMIT ? [["LIMIT", 1]]
Cell Load (0.1ms) SELECT "cells".* FROM "cells" WHERE "cells"."human_id" = ? [["human_id", 3]]
=> [#<cell:0x007fbac8b20590 id:="" 2,="" human_id:="" 3,="" created_at:="" sun,="" 30="" jul="" 2017="" 15:14:21="" utc="" +00:00,="" updated_at:="" sun,="" 30="" jul="" 2017="" 15:14:21="" utc="" +00:00="">,
#<cell:0x007fbac8b20068 id:="" 3,="" human_id:="" 3,="" created_at:="" sun,="" 30="" jul="" 2017="" 15:14:24="" utc="" +00:00,="" updated_at:="" sun,="" 30="" jul="" 2017="" 15:14:24="" utc="" +00:00="">]
[9] pry(main)> binding.trace_tree(htmp: 'rails/count_loaded'){hu.cells.count}
(35.4ms) SELECT COUNT(*) FROM "cells" WHERE "cells"."human_id" = ? [["human_id", 3]]
=> 2</cell:0x007fbac8b20068></cell:0x007fbac8b20590></human:0x007fbac7a210a0>
未加载的调用栈
已加载的调用栈
size
对于size,未加载时发起select count,已加载时直接获取集合的大小
单表时
[52] pry(main)> hus = Human.where('id < ?', 10);
[53] pry(main)> binding.trace_tree(htmp: 'rails/single_size'){hus.size}
(28.7ms) SELECT COUNT(*) FROM "humen" WHERE (id < 10)
=> 2
[54] pry(main)> hus
Human Load (0.2ms) SELECT "humen".* FROM "humen" WHERE (id < 10)
=> [#<human:0x007fbac8718128 id:="" 3,="" name:="" "jack",="" created_at:="" sun,="" 30="" jul="" 2017="" 15:14:01="" utc="" +00:00,="" updated_at:="" sun,="" 30="" jul="" 2017="" 15:14:01="" utc="" +00:00="">,
#<human:0x007fbac8703f20 id:="" 6,="" name:="" "ken",="" created_at:="" tue,="" 01="" aug="" 2017="" 13:30:47="" utc="" +00:00,="" updated_at:="" tue,="" 01="" aug="" 2017="" 13:30:47="" utc="" +00:00="">]
[55] pry(main)> binding.trace_tree(htmp: 'rails/single_size_load'){hus.size}
=> 2</human:0x007fbac8703f20></human:0x007fbac8718128>
未加载的调用栈
已加载的调用栈
源码如下
def size
loaded? ? @records.length : count(:all)
end
关联时
[10] pry(main)> hu = Human.first
Human Load (0.1ms) SELECT "humen".* FROM "humen" ORDER BY "humen"."id" ASC LIMIT ? [["LIMIT", 1]]
=> #<human:0x007fbac8c0e038 id:="" 3,="" name:="" "jack",="" created_at:="" sun,="" 30="" jul="" 2017="" 15:14:01="" utc="" +00:00,="" updated_at:="" sun,="" 30="" jul="" 2017="" 15:14:01="" utc="" +00:00="">
[11] pry(main)> binding.trace_tree(htmp: 'rails/size'){hu.cells.size}
(31.7ms) SELECT COUNT(*) FROM "cells" WHERE "cells"."human_id" = ? [["human_id", 3]]
=> 2
[12] pry(main)> hu = Human.first;hu.cells
Human Load (0.1ms) SELECT "humen".* FROM "humen" ORDER BY "humen"."id" ASC LIMIT ? [["LIMIT", 1]]
Cell Load (0.1ms) SELECT "cells".* FROM "cells" WHERE "cells"."human_id" = ? [["human_id", 3]]
=> [#<cell:0x007fbac8f5c410 id:="" 2,="" human_id:="" 3,="" created_at:="" sun,="" 30="" jul="" 2017="" 15:14:21="" utc="" +00:00,="" updated_at:="" sun,="" 30="" jul="" 2017="" 15:14:21="" utc="" +00:00="">,
#<cell:0x007fbac8f4bcf0 id:="" 3,="" human_id:="" 3,="" created_at:="" sun,="" 30="" jul="" 2017="" 15:14:24="" utc="" +00:00,="" updated_at:="" sun,="" 30="" jul="" 2017="" 15:14:24="" utc="" +00:00="">]
[13] pry(main)> binding.trace_tree(htmp: 'rails/size_loaded'){hu.cells.size}
=> 2</cell:0x007fbac8f4bcf0></cell:0x007fbac8f5c410></human:0x007fbac8c0e038>
未加载的调用栈
已加载的调用栈
关键源码如下
# activerecord-5.1.2/lib/active_record/associations/collection_association.rb
def size
if !find_target? || loaded?
target.size
elsif !association_scope.group_values.empty?
load_target.size
elsif !association_scope.distinct_value && target.is_a?(Array)
unsaved_records = target.select(&:new_record?)
unsaved_records.size + count_records
else
count_records
end
end
# activerecord-5.1.2/lib/active_record/associations/association.rb
def find_target?
!loaded? && (!owner.new_record? || foreign_key_present?) && klass
end
length
而length,未加载时发起select *,然后获取集合大小;已加载时直接获取集合的大小
单表时
[56] pry(main)> hus = Human.where('id < ?', 10);
[57] pry(main)> binding.trace_tree(htmp: 'rails/single_length'){hus.length}
Human Load (32.5ms) SELECT "humen".* FROM "humen" WHERE (id < 10)
=> 2
[58] pry(main)> hus
=> [#<human:0x007fbac7a89588 id:="" 3,="" name:="" "jack",="" created_at:="" sun,="" 30="" jul="" 2017="" 15:14:01="" utc="" +00:00,="" updated_at:="" sun,="" 30="" jul="" 2017="" 15:14:01="" utc="" +00:00="">,
#<human:0x007fbac90a0240 id:="" 6,="" name:="" "ken",="" created_at:="" tue,="" 01="" aug="" 2017="" 13:30:47="" utc="" +00:00,="" updated_at:="" tue,="" 01="" aug="" 2017="" 13:30:47="" utc="" +00:00="">]
[59] pry(main)> binding.trace_tree(htmp: 'rails/single_length_load'){hus.length}
=> 2</human:0x007fbac90a0240></human:0x007fbac7a89588>
未加载的调用栈
已加载的调用栈
关键源码如下
# activerecord-5.1.2/lib/active_record/relation/delegation.rb
delegate :to_xml, :encode_with, :length, :each, :uniq, :to_ary, :join,
:[], :&, :|, :+, :-, :sample, :reverse, :compact, :in_groups, :in_groups_of,
:to_sentence, :to_formatted_s, :as_json,
:shuffle, :split, :index, to: :records
# activerecord-5.1.2/lib/active_record/relation.rb
def records
load
@records
end
def load(&block)
exec_queries(&block) unless loaded?
self
end
关联时
[14] pry(main)> hu = Human.first
Human Load (0.1ms) SELECT "humen".* FROM "humen" ORDER BY "humen"."id" ASC LIMIT ? [["LIMIT", 1]]
=> #<human:0x007fbac8912fc8 id:="" 3,="" name:="" "jack",="" created_at:="" sun,="" 30="" jul="" 2017="" 15:14:01="" utc="" +00:00,="" updated_at:="" sun,="" 30="" jul="" 2017="" 15:14:01="" utc="" +00:00="">
[15] pry(main)> binding.trace_tree(htmp: 'rails/length'){hu.cells.length}
Cell Load (31.3ms) SELECT "cells".* FROM "cells" WHERE "cells"."human_id" = ? [["human_id", 3]]
=> 2
[16] pry(main)> hu = Human.first;hu.cells
Human Load (0.1ms) SELECT "humen".* FROM "humen" ORDER BY "humen"."id" ASC LIMIT ? [["LIMIT", 1]]
Cell Load (0.1ms) SELECT "cells".* FROM "cells" WHERE "cells"."human_id" = ? [["human_id", 3]]
=> [#<cell:0x007fbac9061ea0 id:="" 2,="" human_id:="" 3,="" created_at:="" sun,="" 30="" jul="" 2017="" 15:14:21="" utc="" +00:00,="" updated_at:="" sun,="" 30="" jul="" 2017="" 15:14:21="" utc="" +00:00="">,
#<cell:0x007fbac9061950 id:="" 3,="" human_id:="" 3,="" created_at:="" sun,="" 30="" jul="" 2017="" 15:14:24="" utc="" +00:00,="" updated_at:="" sun,="" 30="" jul="" 2017="" 15:14:24="" utc="" +00:00="">]
[17] pry(main)> binding.trace_tree(htmp: 'rails/length_loaded'){hu.cells.length}
=> 2</cell:0x007fbac9061950></cell:0x007fbac9061ea0></human:0x007fbac8912fc8>
未加载的调用栈
已加载的调用栈
关键源码如下
# activerecord-5.1.2/lib/active_record/relation/delegation.rb
delegate :to_xml, :encode_with, :length, :each, :uniq, :to_ary, :join,
:[], :&, :|, :+, :-, :sample, :reverse, :compact, :in_groups, :in_groups_of,
:to_sentence, :to_formatted_s, :as_json,
:shuffle, :split, :index, to: :records
# activerecord-5.1.2/lib/active_record/associations/collection_proxy.rb
def records
load_target
end
# activerecord-5.1.2/lib/active_record/associations/collection_association.rb
def load_target
if find_target?
@target = merge_target_lists(find_target, target)
end
loaded!
target
end