N+1問題
N+1問題についてどういう時にどこで発生してしまうのか、includeメソッドを使うと何故解決できるのか今ひとつピンときていなかったので調べました。
N+1問題とは?
レコードの数+1回SQLが発行されてしまう問題。
いつどこで起きるか?
N+1問題は2つのモデル同士がhas_many、belongs_to関係がある時に、ビューでeach処理でループさせる中でアソシエーションを使って出力させる場合などに起こりやすい。
具体的にはtweetモデルとuserモデルがそれぞれ、belongs_to :user, has_many :tweetsの関係にあったとすると、tweetコントローラーで@tweets = Tweet.all として、ビューのeach処理の中でtweet.user.nicknameのようにアソシエーション利用しようとすると発生する。
この時にUserモデルを事前にローディングしていないので表示しようとするたびにSQLが発行されてしまう。
(レコードの数がN個ならN回eachで回されるので、アソシエーション利用したtweet.user.nicknameのたびにSELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT 1 [["id", user_id]] のクエリがN回、@tweets.eachのところでSELECT “tweets”.* FROM “tweets”のクエリが1回の、合計N+1回のクエリが発行されている)
防ぐには?
includeメソッドでコントローラーで変数を渡す時点で関連するモデルの情報も事前に読み込んでおくことで、変数@tweetsのプロパティのなかにuserテーブルの情報も含んだ状態でビューに変数@tweetsを渡せるのでN+1問題を防げる。