
RailsのActiveRecordでレコードの件数を調べるときにcount, size, lengthと、件数を取得するメソッドが3種類もあって、どれを使ったら良いのかわかりません。こういうときにはこれを使うべきというのがあれば教えて下さい!
こんな悩みを解決していきます。
- count ,size, lengthの違いを解説
- 悩んだ時はこれを使うべきというのがわかるようになる
RailsにはActive Recordという、データベースのデータを簡単に取得出来るようにしてくれる機能が搭載されています。
今回はそのActive Recordのメソッドで、データの件数を取得するための下記の3つについて見て行きます。
- count
- length
- size
これらのメソッドはどれも結果としては件数が取得出来ますが、内部で行われている処理には違いがあります。
今回の記事を読む事でこの3つのメソッドの内部的な処理の違いを理解し、データベースにも優しい処理を書けるようになりましょう。
目次
countは必ずクエリを発行する
今回の記事ではRails Consoleを活用して、Usersテーブルのレコードを全件取得した後に各メソッドを実行したらどの様に処理されるかを見ていきます。
まずはcountメソッドの挙動です。
irb(main):001:0> users = User.all #usersテーブルのレコードを全件取得
irb(main):002:0> users.count #取得したレコードの件数を取得
(0.6ms) SELECT COUNT(*) FROM `users`
=> 3
irb(main):003:0> users.count #取得したレコードの件数を再度取得
(1.9ms) SELECT COUNT(*) FROM `users`
=> 3
ここで注目して欲しいのはcountメソッドを実行すると毎回データベースに対してクエリを投げているという点です。
SELECT COUNT(*) FROM `users`
基本無いとは思いますが、eachなどのループ処理の中でcountを実行する際にな十分注意しましょう。
- 実行時に必ずデータベースに対してクエリを発行する
- 毎回クエリを発行するため、必ず最新のレコードの件数を取得出来る
lengthはメモリに情報をロード(キャッシュ)する
irb(main):001:0> users = User.all #usersテーブルのレコードを全件取得
irb(main):002:0> users.length #取得したレコードの件数を取得
User Load (0.3ms) SELECT `users`.* FROM `users`
=> 3
irb(main):003:0> users.length #取得したレコードの件数を再度取得
=> 3
lengthは1つめのcountと違い実行時にメモリに情報を保存します。
そのため、2回目以降のlengthではSELECT users
.* FROM users
が実行(表示)されていない点に注目しましょう。
- 初回のlength実行時にデータをメモリに保存している
- 一度データをロードした後は何度lengthを実行しても新たなクエリは発行されない
sizeはlengthとcountのあわせ技
irb(main):001:0> users = User.all #usersテーブルのレコードを全件取得
irb(main):002:0> users.size #取得したレコードの件数を取得
(0.6ms) SELECT COUNT(*) FROM `users`
=> 3
irb(main):003:0> users.size #取得したレコードの件数を再度取得
(0.7ms) SELECT COUNT(*) FROM `users`
=> 3
sizeメソッドはデータをallで取得した後に実行するとcountと同様に必ずクエリを発行します。
しかし下記の様に間にlengthを挟みメモリ上にデータを乗せておくと、sizeもlengthと同様にメモリから取得するようになりクエリを発行しなくなります。
irb(main):001:0> users = User.all
irb(main):002:0> users.length
User Load (0.3ms) SELECT `users`.* FROM `users`
=> 3
irb(main):003:0> users.size
=> 3
- メモリにデータが無い場合はCountと同じくクエリを発行する
- メモリにデータがある場合はlengthと同じくクエリを発行せずに件数を取得可能
loadメソッドでメモリにデータを格納する

lengthを使えばメモリ上にデータが乗るからクエリが発行しなくなるのは分かったけど、lengthを使ってメモリに乗せて、その次からはcountを実行するのはなんか微妙な気がする…
こういう風に思った方もいると思います。
そんな時はloadメソッドを活用してメモリ上にデータを乗せてからsizeを使う方法があります。
irb(main):001:0> users = User.all
irb(main):002:0> users.load
User Load (0.3ms) SELECT `users`.* FROM `users`
=> #, #, #]>
irb(main):003:0> users.size
=> 3
irb(main):004:0> users.size
=> 3
loadメソッドを使った後はlengthの時と同様に、sizeで件数を取得してもクエリが発行されないようになります。
件数を取得するだけならメソッドチェーン
irb(main):001:0> count = User.all.count
(0.7ms) SELECT COUNT(*) FROM `users`
=> 3
今までのコードでは一度usersという変数にallの結果を入れてからcountなどの件数を取得するメソッドを実行していました。
しかしレコードの内容は不要で件数だけが必要な場合は、メソッドチェーンを使うことでよりシンプルに書けます。
件数を取得する場合はどのメソッドを使うべきか
基本的にはlengthとcountの合せ技のsizeを使うのが安定ですが、1つだけ注意点があります。
それはデータがメモリ上に乗っている(キャッシュされている)場合は最新の件数を取得出来ない点です。
irb(main):001:0> users = User.all.load #usersテーブルのレコードを全件取得しキャッシュする
User Load (0.3ms) SELECT `users`.* FROM `users`
irb(main):002:0> users.size #キャッシュの情報を元に件数を取得するのでクエリは発行されない
=> 3
irb(main):003:0> User.create(id: 4) #1件ユーザーのレコードを追加する
(0.2ms) BEGIN
User Create (0.3ms) INSERT INTO `users` (`id`, `created_at`, `updated_at`) VALUES (4, '2021-01-16 14:56:19.564822', '2021-01-16 14:56:19.564822')
(1.1ms) COMMIT
=> #
irb(main):004:0> users.size #usersの情報はキャッシュされているので件数は3件のまま
=> 3
irb(main):005:0> users.count #実際は1件レコードを作成したので4件存在する
(0.6ms) SELECT COUNT(*) FROM `users`
=> 4
countは必ずクエリを発行するため、処理の途中でデータがインサート(作成)された場合でも常に最新の件数を取得することが出来ます。
一方sizeはデータがキャッシュされている場合は、クエリを発行せずにキャッシュから件数を取得するために必ずしも正確な件数を取得出来ない場合があります。
何も考えずにsizeメソッドを使った場合にはこの様な罠があるという事を覚えておきましょう。
メソッドは理解した上で使うべき
似たような処理を行うメソッドでも内部の処理は大きく異なります。
具体的にどのような処理が行われているかを意識しその時の最適なメソッドを選択する事で、パフォーマンスを意識したより良い実装を出来るようになりましょう。
- Ruby初心者に向けた学習ロードマップ!挫折しないための学習方法!
- 【初心者向け】MacでRailsを使えるようにするための環境構築方法を徹底解説!
- 【初心者向け】RailsでMySQLを使うための手順をコマンド付きで解説!
- Railsが難しい理由を現役エンジニアが解説!学習効率を上げるには○○を高めるべき!
- Rails初心者に向けたコマンドまとめ!newやgenerateコマンド、簡単に使える便利関数を紹介!
- Railsでルーティングを作成!ネストやパラメーターを取得する方法も解説!
- Railsの部分テンプレートの書き方をコード付きで解説!引数の渡し方やディレクトリ名の悩みとはおさらば!
- Railsでリンクヘルパー(link_to)を使ってサクッとリンクを生成する方法を解説!