前回の記事では、joins
とleft_outer_joins
の挙動の違いについてまとめました。
前回の記事をまだ読んでいない方はこちらからどうぞ。
【第1回】RailsのN+1問題と解決するためのメソッド完全版
【第2回】Railsのjoinsとleft_outer_joinsの挙動と違いをまとめた
##本記事の内容
- joins
- left_outer_joins
- eager_load <=(本記事ではここから解説)
- preload
- includes
##eager_load
関連モデルを一括読み込みするメソッドです。
SQLのLEFT OUTER JOIN句
を発行し関連付けをキャッシュします。
キャッシュとは一度取得したデータをメモリ上に一時的に保存する事で、次回のデータ取得時により高速にデータ取得する仕組みです。
eager_load
を使う事で、あるモデルを軸にその関連モデルの値を取得したい場合のN+1問題の解消ができる様になります。
モデル名.eager_load(:関連モデル)
eager_load
は、関連モデルのデータをメモリに一時的に保存することで、メモリからデータを取得します。
関連モデルに存在する属性を、ループ処理の中で取得したい場合に使用します。
joins
とleft_outer_joins
との違いは、関連モデルを一括でキャッシュすることです。
###使い方
User.eager_load(:post).each do |user|
p user.posts.title
end
Userモデル
と関連するPostモデル
を一括読み込みしたうえでeachメソッド
でループ処理を実行しています。
キャッシュされたPostモデル
を使用するので、DBへのアクセスはされません。
そのためSQLが発行されず、N+1問題が解消されるわけですね。
##preload
同じく関連モデルを一括読み込みするメソッドです。
eager_load
との違いは、SQLをモデルごとに発行し、関連モデルをキャッシュすることです。
つまり、eager_load
のSQL発行回数が1回に対して、preload
は軸となるモデルと関連モデルの2つに対してSQLを発行します。合計2回です。
preload
は対象テーブルのデータサイズが大きくJOINのコストが大きい場合に有効です。
###使い方
モデル名.preload(:関連モデル)
関連モデルに存在する属性を、ループ処理の中で取得したい場合に使用します。
User.preload(:post).each do |user|
p user.posts.title
end
注意点としては、モデルごとにSQLを発行するので、eager_load
と異なり絞り込み条件を指定できません。
##includes
includes
はSQL発行時の条件に合わせて、eager_load
かpreload
どちらかを使用します。
N+1問題を解消するとき、とりあえずincludes
を使っているという人も多いのではないでしょうか。
たしかに便利なメソッドですが、eager_load
かpreload
のどちらの挙動を狙った実装なのかわからないので、最終的にパフォーマンスが落ちてしまう可能性があることに注意が必要です。
個人的にはincludes
は非推奨です。eager_load
かpreload
の挙動を理解したうえで実装するのがベストです。
###使い方
モデル名.includes(:関連モデル)
includes
メソッドでは以下の条件に合致する場合にはeager_loadの挙動を、合致しない場合はpreloadの挙動を行います。
- 引数に指定した関連テーブルに対しjoinsメソッドを使用している場合
- 引数に指定した関連テーブルに対しwhereメソッドを使用している場合
- 引数に指定した関連テーブルに対しreferencesメソッドを使用している場合
class PostsController < ApplicationController
def index
@posts = Post.includes(:photos, :user).order('created_at DESC').page(params[:page]).per(3)
end
end
このように実装した場合には、上記3つのメソッドは使用されていないのでpreload
が実行されているということになります。
##まとめ
-
eager_load
は、関連モデルのデータをメモリに一時的に保存することで、メモリからデータを取得する。 -
preload
は、SQLをモデルごとに発行し、関連モデルをキャッシュする。 -
includes
はSQL発行時の条件に合わせて、eager_loadかpreloadどちらかを使用する。
ふぅー。やっとすべて解説できました。
「まだよくイメージがわかない」という人も手元の環境で色々試してみると、理解が加速するはずです。
ぜひ試してみてください。