LoginSignup
7
2

More than 5 years have passed since last update.

Relation or Enumerable?

Last updated at Posted at 2018-12-09

Railsで引いてきたリレーションには、Enumerableとして使うこともできます。状況によって、どちらがいいのか考えてみましょう。

概論

リレーションとして使う

Model.where(条件)としてもその場でSQLが実行されるわけではなく、必要になるギリギリまで遅延されます。データとして欲しい最終形によって、

  • モデルの配列のように使う
  • countsumでDBレベルの集計をかけて、結果を取る
  • pluckで必要な列だけ抽出する

のように、多様なアクセスができます。集計はDBレベルで行われますので、SQL文を投げることとなります。

Enumerableとして使う

上で「モデルの配列のように使う」とありましたが、.eachもありますし、Enumerableとして.mapなどの集計メソッドを使うことも可能です。これはいったんテーブルを引いて、結果を全部展開してから実行します。

ケーススタディ

巨大なデータセットの場合→リレーションとして処理

処理すべきデータが数十万件あるような場合には、Enumerableとして処理しようとすれば、そのデータを全件転送する必要がありますが、転送だけでも明らかに負担となります。このような場合は、リレーションのまま集計関数をDBに投げて、結果だけ取得したほうが効率的です。

件数取得→sizeがよさげ

リレーションの長さを取得するには、countlengthsizeの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を使って問題ありません。

7
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
2