tapitapi’s blog

1日1杯タピオカ!エンジニア

【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 shop

end

 

これで、データベースクエリは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についてもシェアできればと思います^^

 

おやすみなさい