# N+1問題とは
必要以上にSQLが発行されてしまいアプリのパフォーマンスが低下してしまうこと。
##具体例
テーブルは
OwnerテーブルとPetテーブルがあると仮定
Petテーブルにはowner_idという外部キーが設定されていて、下記のように関連付けているという前提にします
-------オーナーモデルクラス---------
class Owner < ApplicationRecord
belongs_to :pet
end
-------ペットモデルクラス---------
class Pet < ApplicationRecord
has_many :owner
end
まず下記のallメソッドをすると全件取得するためにSQLが発行されDBに1回アクセスします。
owners = Owner.all
下記は全件取得したownersをeachで一つずつ取り出して取り出したownerに関連づいてるpetの名前を全件取得し、eachでひとつずつ取り出し出力しています。
#ownerがに関連している(飼っている)ペットの名前を取得している
owners.each do |owner|
owner.pets.name do |pet_name|
puts pet_name
end
end
上記のコードの裏ではまずownerに関連づいたpetのデータを取得するためにSQLが発行されます。このownerに関連づいたpetのデータを取ってくる作業をownerの数だけ行うので大量に関連づいたデータを取得するためにデータへのアクセスが行われます。
つまり、owner1回のデータベースへのアクセスに対して関連づいたデータベースへのアクセスが複数(N)回行われる
これを
N + 1 問題
といいます
##解決方法
includesを使った解決方法
先ほどのコードを書き換えます
owners = Owner.includes(:pets)
owners.each do |owner|
owner.pets.each do |pet|
puts pet.name
end
end
まず1行目はallの部分をincludesに変えています。
これでOwnerを全件取得して取得したOwnerに関連するpetsのデータを取ってきています。
SQLは具体的に
・Ownerの全件取得
・取得したOwnerに関連したpetsを取得
この二つのSQLが発行されます。
これで関連づいたpetのデータをまとめて取得しているので4行目のeachではSQLは発行されずさきほど発行したSQLの2回のみでデータを扱うことができます。
includesを事前にまとめて関連データを取得することでSQLを複数回呼び出さずに2回のSQLのみで済ませることができました。