0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

N + 1問題とは

Posted at

はじめに

N + 1問題については、とても参考になるまとまった記事がたくさんあるのですが、自分なりに簡単にまとめてみました。解釈が間違っていましたらご指摘ください。

N + 1問題とは

簡単に言えば、SQLを重複して発行してしまうこと。

例えば投稿アプリで投稿一覧ページがあった場合にユーザーの名前を表示したい場合

post_controller.rb
def index
  @posts = Post.all
end
post_index.erb
<% @posts.each do |post| %>
  <%= post.user.name %>
<% end %>

のようにすると思います。
この場合発行されるSQLは以下のようになります。

  1. @posts = Post.allで投稿を全て取得
  2. post.user.nameで投稿に紐づくユーザーを取得

2.の、投稿に紐づくユーザーを取得するとき、画面に表示される投稿の数分、投稿に紐づくユーザーを一回一回SQLを発行して取得してきます。

つまり投稿が10件の場合、まずPostを全て取得するSQLと、投稿の数分(N)のSQLが発行されることになり、これが「N + 1」問題と呼ばれています。

これではデータの数が多いほど、パフォーマンスが低下するのは明白です。
そのため、N + 1問題が起きてしまった場合はpreloadメソッドか、eager_loadメソッドを使用することで解決します。

preload

preloadメソッドを使用して引数にアソシエーションを渡すことで、Postのデータとともに関連するuserのデータも取得してくれます。
SQLの発行回数は、Postのデータ取得で1回、Postに関連するuserの取得で2回となります。

post_controller.rb
def index
  @posts = Post.preload(:user)
end

eager_load

eager_loadメソッドも、同様にアソシエーションを取得してくれます。
こちらはテーブル同士を一つに結合させてしまい、viewで呼び出す際にその結合したテーブルから得たデータを取得します。
SQLは1回だけです。

post_controller.rb
def index
  @posts = Post.eager_load(:user)
end

どちらがいいのか

調べてみた感じでは、「こっちを使った方がいい」というような偏った意見は見かけませんでした。
テーブルを結合させるのかさせないのかなど、アプリが複雑で大規模なものであれば考える必要がある印象です。個人開発であれば取り敢えずeager_loadメソッドを使っておけばいいのかな?と思います。

参考

ActiveRecordのjoinsとpreloadとincludesとeager_loadの違い
【Ruby on Rails】n+1 問題とは?

0
0
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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?