【Ruby on Rails】Includesを使おう!N+1問題対策
Ruby on RailsでIncludesを使おう!という紹介。
Includesを使用することで、データベースとのやりとりを少なくすることができます
今回は、お店のオーナーを管理するテーブル(Owner)と
お店を管理するテーブル(Shop)があり、
オーナーとお店をDBから取ってくる場合を想定してみます
Owner table(ジョンさんとケンさんとアリスさん)
id:100, name: ジョン
id:101, name: ケン
id:105, name: アリス
Shop table(ジョンさんはマック、ケンさんはケンタ、アリスさんはスタバのオーナー)
id:1, name: マクドナルド, owner_id:100
id:2, name: ケンタッキー, owner_id:101
id:3, name: スターバックス, owner_id:105
Includeを使わずにオーナーとお店を取ってくるコード
@owners = Owner.all
@owners.each do |owner|
shop = owner.shops
p shopend
これで、データベースクエリは4回実行される
SELECT "owners".* FROM "owners"
// オーナー全員が検索される
SELECT "shops".* FROM "shops" WHERE "shops"."user_id" = 100
// id:1, name: マクドナルド, owner_id:100 が検索される
SELECT "shops".* FROM "shops" WHERE "shops"."user_id" = 101
// id:2, name: ケンタッキー, owner_id:101が検索される
SELECT "shops".* FROM "shops" WHERE "shops"."user_id" = 105
// id:3, name: スターバックス, owner_id:105が検索される
オーナーがN人いれば、N+1回のクエリが実行されてしまう、、、!!!
これがN+1問題と言われ、検索に時間がかかってしまう原因となってしまいます。
ここで、Includesを使ったコードを見てみます。
@owners = Owner.includes(:shops).all
@owners.each do |owner|
p owner.shops
end
どんなにオーナーが増えても、データベースクエリは2回実行で済みます
SELECT "users".* FROM "users"
// オーナー全員が検索される
SELECT "shops".* FROM "shops" WHERE "shops"."user_id" IN (100, 101, 105)
// オーナーに紐づいたお店全てが検索される
@owners = Owner.includes(:shops).where("id > ?", 101)
の様に、オーナーを選別してから、それに紐づくお店を検索することもできます
以上ですー!!
今後もIndexについてなど、DB関連のTipsについてもシェアできればと思います^^
おやすみなさい