includesメソッドとは
アソシエーションの関連付けを事前に取得しN +1問題
を解決してくれるメソッドのこと。
N + 1問題
SQLが大量に発行されることで動作が重くなる問題のこと
なぜSQLが大量に発行されるのか?
(例)
user_idにuserテーブルのidを外部キーとして参照するfruitsテーブルがあったとして・・・
fruitsテーブル
id | name | user_id |
---|---|---|
1 | りんご | 2 |
2 | みかん | 3 |
3 | バナナ | 1 |
4 | マンゴー | 2 |
userテーブル
1 | 2 |
---|---|
1 | タケシ |
2 | サトシ |
3 | カスミ |
UserモデルはFruitモデルに対して1対他の関係になっている
class User < ActiveRecord::Base
has_many :fruits
end
class Fruits < ActiveRecord::Base
belongs_to :user
end
この関係で、「全てのユーザーのもってるフルーツを一覧表示する」コードを以下のように書くと、
userテーブルに1回アクセスが行われる(=SQLが発行される。)
@users = User.all
また、ビューで以下のように記述して一覧表示すると、user1つずつに対してfruitsテーブルにアクセスするので、
userテーブルのレコードの数だけ(3回)SQLが発行される。
@users.each do |user|
user.fruits.each do |fruit|
fruit.name
end
end
この1+3回のSQL発行が1+N問題
となる。
includeメソッド
上記の状態で、usersテーブルにアクセスする際に、fruitsテーブルの情報ももってきてくれるのがincludeメソッド
。
引数にアソシエーションで定義した関連名を指定して定義する。(NOTテーブル名)
class User < ActiveRecord::Base
has_many :fruits #←関連名
end
@users = User.all #変更
@users = User.include(:fruits) #変更後
これでusersテーブルへのアクセス1回、fruitsテーブルへのアクセスは1回の計2回に変更される。
コントローラーの時点で関連テーブルの情報を一括取得することで、ビューが動いた時に必要以上のSQLが発行されない。
includeメソッドの親子関係
次に、このFruitモデルのidをさらに外部キーにする関連Juiceモデルがある場合は、以下のように書くことで、親子の子の情報もまとめて取得することができる(便利!)
# User.rb
class User < ActiveRecord::Base
has_many :fruits
end
# fruit.rb
class Fruit < ActiveRecord::Base
belongs_to :user
has_many :Juices
end
# juice.rb
class Juice < ActiveRecord::Base
belongs_to :fruit
end
@users = User.includes(fruits: :juices)
上記を実行すると・・・
SELECT `users`.* FROM `users`
SELECT `fruits`.* FROM `fruits` WHERE `fruits`.`user_id` IN (1, 2, 3)
SELECT `juices`.* FROM `juices` WHERE `juices`.`fruit_id` IN (1, 2, 3, 4)
3回のアクセスで全て取得できる!!
お疲れ様でした。