マッスル・メモリー

筋肉エンジニアのブログ

【Rails】 count, length, sizeの違いと使い分け案

count

キャッシュしない。
毎回sqlのcountを使ってカウントする。
最新の件数を取得できる。

area = Area.first
# 毎回sqlが発行される
area.sub_areas.count
# (0.4ms)  SELECT COUNT(*) FROM `sub_areas` WHERE `sub_areas`.`area_id` = 1
# => 1
area.sub_areas.count
#    (0.4ms)  SELECT COUNT(*) FROM `sub_areas` WHERE `sub_areas`.`area_id` = 1
# => 1

# 毎回sqlが発行されるので最新の件数を取得できる
SubArea.create!(area_id: 1, ja_name: 'hoge')
area.sub_areas.count
# (0.4ms)  SELECT COUNT(*) FROM `sub_areas` WHERE `sub_areas`.`area_id` = 1
# => 2
area.sub_areas.count
# (1.9ms)  SELECT COUNT(*) FROM `sub_areas` WHERE `sub_areas`.`area_id` = 1
# => 2

length

キャッシュする。
2回目以降はクエリが発行されない。
最新の件数は取得されない。

area = Area.first
area.sub_areas.length
# SubArea Load (2.8ms)  SELECT `sub_areas`.* FROM `sub_areas` WHERE `sub_areas`.`area_id` = 1
# => 1

# キャッシュするので2回目以降はクエリが発行されない
area.sub_areas.length
# => 1

SubArea.create!(area_id: 1, ja_name: 'hoge')

# countの場合は最新の件数を取得するが
area.sub_areas.count
# (0.4ms)  SELECT COUNT(*) FROM `sub_areas` WHERE `sub_areas`.`area_id` = 1
# => 2
# 
# lengthの場合は最新の件数を取得しない
area.sub_areas.length
# => 1

size

メモリにデータがあればそれを参照し、なければクエリを発行し続けて最新のデータを取得する。

area = Area.first
# クエリを発行して最新のデータを取得する
area.sub_areas.size
# (0.4ms)  SELECT COUNT(*) FROM `sub_areas` WHERE `sub_areas`.`area_id` = 1
# => 1
area.sub_areas.size
# (0.4ms)  SELECT COUNT(*) FROM `sub_areas` WHERE `sub_areas`.`area_id` = 1
# => 1

SubArea.create!(area_id: 1, ja_name: 'hoge')
area.sub_areas.count
# (1.4ms)  SELECT COUNT(*) FROM `sub_areas` WHERE `sub_areas`.`area_id` = 1
# => 2
area.sub_areas.size
# (0.4ms)  SELECT COUNT(*) FROM `sub_areas` WHERE `sub_areas`.`area_id` = 1
# => 2
area.sub_areas.size
# (0.4ms)  SELECT COUNT(*) FROM `sub_areas` WHERE `sub_areas`.`area_id` = 1
# => 2

areas = Area.all
areas.count
# (3.7ms)  SELECT COUNT(*) FROM `areas`
# => 17
areas.length
# => 17
# メモリにデータがある場合はそれを参照する
areas.size
# => 17

使い分けの案

最新のデータを取得したい時はcount。
最新のデータを取得する必要がない時、eachなどの繰り返し処理で使う時はlength。
sizeはキャッシュの状況によって発行されるクエリが変わるので個人的に使いずらい。