Railsが自動的に生成するfollowing_ids
メソッドを呼ぶと、以下のようなSQLがデータベースに発行されます。
sql
1SELECT "users"."id" FROM "users" INNER JOIN "relationships" ON "users"."id" = "relationships"."followed_id" WHERE "relationships"."follower_id" = ?
その実行結果をもとに、Micropost.where
でメソッドが以下のようなSQLをデータベースに発行します。
sql
1SELECT "microposts".* FROM "microposts" WHERE (user_id IN (409608538,659682706)
2OR user_id = 762146111) ORDER BY "microposts"."created_at" DESC
ちなみに上の409608538,659682706
の部分が最初のSQLで取得した実行結果です。
つまり、この場合はデータベースへの問い合わせが2回発生します。
一方で、
sql
1following_ids = "SELECT followed_id FROM relationships
2 WHERE follower_id = :user_id"
3Micropost.where("user_id IN (#{following_ids})
4 OR user_id = :user_id", user_id: id)
と、書いた場合は次のようなSQLを1回発行するだけで同じ結果が得られます。
sql
1SELECT "microposts".* FROM "microposts" WHERE (user_id IN (SELECT followed_id FROM relationships
2WHERE follower_id = 762146111)
3OR user_id = 762146111) ORDER BY "microposts"."created_at" DESC
データベースへの問い合わせ処理は比較的コストが高いので、なるべく減らした方がパフォーマンス面で有利です。
よって、後者の方が効率的、ということになります。
別解
少し高等テクニックになりますが、次のように書く方法もあります。
ruby
1# following_idsはActiveRecord::Relationオブジェクトで、最終的にSQL(サブクエリ)に展開される
2following_ids = self.following.select(:id)
3Micropost.where("user_id IN (:following_ids)
4 OR user_id = :user_id", following_ids: following_ids, user_id: id)
こうすると次のようなSQLが1回発行されます。実行結果は同じです。
sql
1SELECT "microposts".* FROM "microposts" WHERE (user_id IN (SELECT "users"."id" FROM "users" INNER JOIN "relationships" ON "users"."id" = "relationships"."followed_id" WHERE "relationships"."follower_id" = 762146111)
RailsやSQLに不慣れだとピンと来ないかもしれませんが、熟練者は堅牢性や再利用性の観点からなるべく手でSQLを書くよりもActiveRecordのクエリインターフェースを活用しようとするはずです。
知識として、こういった書き方があることも頭の片隅に置いておくと良いかもしれません。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2020/06/01 05:17