N+1問題について
要約
関連テーブルからデータを取得するときに、結合せずに、1つづつデータを取ると、負荷がかかる。
1つづつデータを取る場合とは、まず親となるテーブルから対象のデータ(N個)を取得するSQLが1回、 子テーブルの外部キーを条件にデータを取得するSQLがN回、合計1+N回SQLを発行する事になる。 このやり方だと負担がかかる。 対応策は必要なレコードはまとめて取得することを意識する。
データを取得する状況を考える
例えば、CompaniesテーブルとUsersテーブルがあって、 CompaniesはたくさんのUserを持っている状態とする。 各Companiesに紐づくUserのデータを表示したい場合を考える。
N+1の取り方
Comapaniesを取ってくる
Comapany.all
次に取ってきたCompaniesをeachで回してUsersの外部キーを条件に名前を取得する
Company.all.each do |c| puts User.find_by(company_id:c.id) end
そうすると以下のようにSQLが複数発行される
# usersテーブルから取得(1回目) SELECT `users`.* FROM `users` WHERE `users`.id = 1 LIMIT 1 # usersテーブルから取得(2回目) SELECT `users`.* FROM `users` WHERE `users`.id = 2 LIMIT 1 # usersテーブルから取得(3回目) SELECT `users`.* FROM `users` WHERE `users`.id = 3 LIMIT 1
全体のCompanyが1回、NこのCompanyでN回、合計1+N回SQLが発行される。
対応策
テーブルを結合させて、まとめて取得する
Rails における内部結合、外部結合まとめ - Qiita
例えば、joinsを使うと以下のようになる。
Comapny.joins(:user)