1
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が発行されてしまい、動作が悪くなってしまう問題のこと。
必要以上にSQLが発行されると時間が掛かり、これが数回なら特に動作に影響はないが、何万回と回数が増えると動作が重くなってしまう。

##N+1問題を実際に見てみる
※ここではテーブルのアソシエーションについての説明は省く。

ネコの飼い主のテーブルがownersテーブル、ネコの名前を扱うテーブルがcatsテーブルの2つのテーブルを例にN +1 問題を確認していく。

「全ての飼い主の猫の一覧」をviewで表示したい場合、controller側で全ての飼い主をallメソッドで取得し、view側で飼い主の持つcatsをアソシエーションによって下記の様に記述。

# controller
@owners = Owner.all

# view
@owners.each do |owner|
  owner.cats.each do |cat|
    cat.name
  end
end

結果:
# controller
SELECT `owners`.* FROM `owners`

# view
SELECT `cats`.* FROM `cats`  WHERE `cats`.`owner_id` = 1
SELECT `cats`.* FROM `cats`  WHERE `cats`.`owner_id` = 2
SELECT `cats`.* FROM `cats`  WHERE `cats`.`owner_id` = 3
SELECT `cats`.* FROM `cats`  WHERE `cats`.`owner_id` = 4

その結果、catsテーブルに対して、4回のアクセスが行われている事がわかる。
このようにownersテーブルへのアクセス1回に対して、関連するテーブルがN回発行されている1+Nの状況を「N+1問題」と言う。

この問題を解決すれば、ownersテーブルにアクセスする際に、関連するcatsテーブルのレコードを取得する事が出来れば、発行されるSQLも4回から1回にまとめる事が出来る。

##includesメソッドを使ってN+1問題を解決
includesメソッドで指定された関連付けが最小限のクエリ回数で読み込まれるため、これによってN+1問題を解決する事が出来る。

includesメソッド
includesメソッドは、引数にアソシエーションで定義した関連名を指定して定義。(※テーブル名ではない)

モデル名.includes(:関連名)

先程の飼い主と猫のテーブル例にincludesメソッドを入れると、viewのアクセス数が4回→1回に減っている。

# controller
@owners = Owner.includes(:cats)

# view
@owners.each do |owner|
  owner.cats.each do |cat|
    cat.name
  end
end

結果:
SELECT `owners`.* FROM `owners` 
SELECT `cats`.* FROM `cats`  WHERE `cats`.`owner_id` IN (1, 2, 3, 4...)

このように、includesメソッドを使うと関連するテーブルのレコードをまとめて取得させる事によって必要以上のSQLを発行する事なく済むようになる。

##参考記事
【Rails】 N+1問題をincludesメソッドで解決しよう!

1
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
1
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?