N + 1とは?
関連するレコードを無駄に見に行ってしまう事
具体例
前提
UserとAirticleで1対多の関係
- controller
AirticleController def index @airtcles = Airticle.all end
- view
<% @airtcles.each do |airtcle| %> <%= airtcle.user.email%> <% end %>
- 発行されるsql
無駄にAirticleの件数分だけ、userにアクセスしてしまう 今回だとAirticleが2件なので、Userテーブルにも2回問い合わせる Airticleが100件だったら Atirtcleに1回 Userに100回問い合わせるのでDBへの問い合わせ回数が101回になる
101回の問い合わせを2回に済ませられる
SELECT 'articles'.* FROM 'articles' # Article.all の実行 SELECT 'users'.* FROM 'users' WHERE 'users'.'id' = 1 LIMIT 1 # article.user.name を実行する際に走る SELECT 'users'.* FROM 'users' WHERE 'users'.'id' = 2 LIMIT 1
解決策
AirticleController def index @airtcles = Airticle.includes(:user) end
- sqlが2回だけ呼ばれる
SELECT 'article'.* FROM 'articles' SELECT 'users'.* FROM 'users' WHERE 'users'.'id' IN (1, 2)
深く理解
これは、INNER JOINが行われている。テーブルの結合
テーブル結合する意味
テーブル結合をしないと、Airticleを見に行って、Userテーブルを見に行く。
テーブルを結合すると一つのテーブルを見にいく感じになる
結合方法には2つある
- inner join
両方のテーブルで一致するモノがあるモノだけを取得
- outer join
両方のテーブルで一致しないモノも含めて取得
参考記事
- inner join
- inner join outter join違い