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?

N + 1問題と対策メソッド

0
Last updated at Posted at 2026-02-01

はじめに

久しぶりにER図や、データ設計について考えることがあったので、理解が若干怪しいN+1問題についてアウトプットしようと思ったので記事にしました
間違っていればコメントいただけると嬉しいです

n+1問題とは

一覧を取得して、ループ処理のたびにクエリが発行されることです
具体例を以下に出します
ユーザー一覧を表示する際に、それぞれの投稿も表示したい場合

users = User.all

users.each do |user|
    puts user.posts
end

最初の1回
SELECT * FROM users で100人を取得したとする

それ以降のn回
ループ処理の中で1人目の投稿を取るために
SELECT * FROM posts WHERE user_id = 1

2人目
SELECT * FROM posts WHERE user_id = 2

これを100人分繰り返す
ユーザーが増えるほど、クエリは増えるのでn+1となる

解決方法

preload

クエリを分割して実行し、子テーブルのデータを取得してキャッシュするメソッドです

users = User.preload(:posts)

users.each do |user|
    puts user.posts
end
SELECT * FROM users
SELECT posts.* FROM posts WHERE posts.user_id IN  (1, 2, 3...)

また、子テーブルでWHERE(絞り込み)した場合はエラーとなります

eager_load

eager_loadは、関連するテーブルをLEFT OUTER JOINで結合し、キャッシュします
最初に関連するテーブルを結合しているので、クエリの発行量が1回で済みます

users = User.eager_load(:posts)

users.each do |user|
    puts user.posts
end
SELECT users.*, posts.* 
FROM users
LEFT OUTER JOIN posts ON posts.user_id = users.id

includes

includesは条件によって、挙動が変わるメソッドです
デフォルトでは、preloadと同じですが、子テーブルの要素でWHEREした時などはeager_loadと同じ挙動をします

[余談] LEFT OUTER JOIN

LEFT OUTER JOINは、指定したテーブル(FROM)を全て含み、結合条件(ON)が一致すれば右側のテーブルにデータが入ります
一致しない場合右のテーブルはNULLとなります

まとめ

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?