書き途中
「N+1問題」とは
SQLクエリを「データ量(N)+1回」投げてしまっている。
簡単にいうと、1回のアクセスで済むのに何回もアクセスしている。
→必要ないアクセスまでしてしまっているためパフォーマンスが低下してしまう。
#原因
アソシエーションを使用する時に発生してしまう。
具体例としてUserモデル(ユーザー)とPostモデル(ユーザーの投稿)をアソシエーションする時を考えてみる。
【アソシエーションの設定】
Userは複数のPostを持っている
class User <ActiveRecord::Base
has_many :posts
end
Postは1つのUserを持っている
class Post < ActiveRecord::Base
belongs_to :user
end
【投稿を表示】
@users = User.all
→全てのユーザーを取得する
@users.each do |user|
puts user.post.content
end
→それぞれのユーザーに対して投稿を取得する
(「user_id=1」の投稿を取得、「user_id=2」の投稿を取得・・・・ N回postテーブルにアクセス)
【SQLで確認】
SELECT 'users'.* FROM 'users'
SELECT 'posts'.* FROM 'posts' WHERE 'posts'.'user_id' = 1
SELECT 'posts'.* FROM 'posts' WHERE 'posts'.'user_id' = 2
SELECT 'posts'.* FROM 'posts' WHERE 'posts'.'user_id' = 3
・
・
・
Userテーブルに1回、PostテーブルにN回アクセスしていることがわかる。
(1+N問題と言った方がわかりやすいかも)
このため、アソシエーションを使用する時は「N+1問題」に注意しなければならない!
#対策
「includes」メソッドを使用する。
「モデルA.include(:モデルB)」 → Aに基づくBを取得
「includes」メソッドを用いてコントローラーを書き換えると
@users = User.includes(:post).all
→全てのユーザーを取得する
→userモデルに基づくpostモデルを取得する
(「user_id=1~N」のデータを取得する)
@users.each do |user|
puts user.post.content
end
【SQLで確認】
SELECT 'users'.* FROM 'users'
SELECT 'posts'.* FROM 'posts' WHERE 'posts'.'user_id' IN (1,2,3・・・)
Userテーブルに1回、Postテーブルに1回のアクセスで済む。