Railsで引いてきたリレーションには、Enumerable
として使うこともできます。状況によって、どちらがいいのか考えてみましょう。
概論
リレーションとして使う
Model.where(条件)
としてもその場でSQLが実行されるわけではなく、必要になるギリギリまで遅延されます。データとして欲しい最終形によって、
- モデルの配列のように使う
-
count
やsum
でDBレベルの集計をかけて、結果を取る -
pluck
で必要な列だけ抽出する
のように、多様なアクセスができます。集計はDBレベルで行われますので、SQL文を投げることとなります。
Enumerable
として使う
上で「モデルの配列のように使う」とありましたが、.each
もありますし、Enumerable
として.map
などの集計メソッドを使うことも可能です。これはいったんテーブルを引いて、結果を全部展開してから実行します。
ケーススタディ
巨大なデータセットの場合→リレーションとして処理
処理すべきデータが数十万件あるような場合には、Enumerable
として処理しようとすれば、そのデータを全件転送する必要がありますが、転送だけでも明らかに負担となります。このような場合は、リレーションのまま集計関数をDBに投げて、結果だけ取得したほうが効率的です。
件数取得→size
がよさげ
リレーションの長さを取得するには、count
、length
、size
の3つのメソッドがあります。
-
count
→DBにCOUNT
クエリを投げる -
length
→(データがなければ全件取得して)数を数える -
size
→データが取得済みならlength
、なければcount
(ソース)
ということで、前後でのリレーションの使用状況が不明ならsize
を使うのが無難な気がします。
リストのhas_many
→Eager Load+Enumerable
で処理する
リストからさらにリレーションをたどるような場合、N+1クエリが問題となりますが、リレーションのままmaximum
などを投げると、これもN+1クエリとなります。includes
などで子リレーションをロードしておいた上で、データアクセスはEnumerable
としてその集合内に対して行う、というようにすれば、DBアクセスの回数は最小限となります。
一部の列のみの取得→Rails 5ではpluck
で問題なし
Rails 4までは、pluck
は必ずデータベースにアクセスするようになっていましたが、Rails 5では、すでにリレーションを読み込み済みの場合、そちらを参照するようになります。なので、リレーションが読み込み済みかどうかにかかわらずpluck
を使って問題ありません。