ActiveRecordでレコードの合計値を取得する場合は、countよりsize使おうという話
結論(仮)
- countよりsize使ったほうが便利
sizeとcountの挙動の違いについて
size
- メモリに乗っているときはクエリ発行せずメモリ上から計算
- なければクエリ発行
size()Link Returns the size of the collection. If the collection hasn't been loaded, it executes a SELECT COUNT(*) query. Else it calls collection.size. If the collection has been already loaded size and length are equivalent. If not and you are going to need the records anyway length will take one less query. Otherwise size is more efficient.
# Person: 1 に対して pets: N class Person < ActiveRecord::Base has_many :pets end # 一度クエリ発行 person.pets # This will execute a SELECT * FROM query # => [ # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>, # #<Pet id: 2, name: "Spook", person_id: 1>, # #<Pet id: 3, name: "Choo-Choo", person_id: 1> # ] # 再度クエリは発行されずcountが返却される / loaded状態 # ActiveRecord Association がメモリに乗っている形 person.pets.size # => 3 # Because the collection is already loaded, this will behave like # collection.size and no SQL count query is executed.
count
- loaded状態でもかならずSQLを発行する
つまりどういうことか
# ここでキャッシュしたい pry(main)> user = User.includes(:article_comments).find(1) User Load (0.4ms) SELECT `users`.* FROM `users` WHERE `users`.`id` = 1 LIMIT 1 ArticleComment Load (0.3ms) SELECT `article_comments`.* FROM `article_comments` WHERE `article_comments`.`user_id` = 1 # キャッシュしたいのに再度クエリが発行されてしまっている pry(main)> user.article_comments.count (0.5ms) SELECT COUNT(*) FROM `article_comments` WHERE `article_comments`.`user_id` = 1 => 1
- なのでincludes等でN + 1クエリを防ごうとしても、都度SQLが実行されてしまう。
参考
メモ
- size / count / lengthの実装を確認