3
5

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 3 years have passed since last update.

「N+1問題」メモ 

Last updated at Posted at 2019-12-31

書き途中

「N+1問題」とは

SQLクエリを「データ量(N)+1回」投げてしまっている。
簡単にいうと、1回のアクセスで済むのに何回もアクセスしている。
 →必要ないアクセスまでしてしまっているためパフォーマンスが低下してしまう。

#原因
アソシエーションを使用する時に発生してしまう。
具体例としてUserモデル(ユーザー)とPostモデル(ユーザーの投稿)をアソシエーションする時を考えてみる。

【アソシエーションの設定】

Userは複数のPostを持っている

user.rb
class User <ActiveRecord::Base
 has_many :posts
end

Postは1つのUserを持っている

post.rb
class Post < ActiveRecord::Base
 belongs_to :user
end

【投稿を表示】

user_controller.rb
@users = User.all

 →全てのユーザーを取得する

view.rb
@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」メソッドを用いてコントローラーを書き換えると

user_controller.rb
@users = User.includes(:post).all

 →全てのユーザーを取得する
 →userモデルに基づくpostモデルを取得する
 (「user_id=1~N」のデータを取得する)

view.rb
@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回のアクセスで済む。

3
5
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
3
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?