確認環境
$ bundle exec ruby --version
ruby 2.7.5p203 (2021-11-24 revision f69aeb8314) [x86_64-darwin19]
$ bundle exec rails --version
Rails 6.0.4.6
使用するActive Recordのリレーション
class SampleUser < ApplicationRecord
has_many :sample_user_items
end
class SampleUserItem < ApplicationRecord
belongs_to :sample_user
end
eager_load
LEFT OUTER JOIN が使われます。
irb(main):013:0> SampleUserItem.eager_load(:sample_user)
SQL (0.2ms) SELECT "sample_user_items"."id" AS t0_r0, "sample_user_items"."sample_user_id" AS t0_r1, "sample_user_items"."price" AS t0_r2, "sample_user_items"."name" AS t0_r3, "sample_user_items"."created_at" AS t0_r4, "sample_user_items"."updated_at" AS t0_r5, "sample_users"."id" AS t1_r0, "sample_users"."name" AS t1_r1, "sample_users"."created_at" AS t1_r2, "sample_users"."updated_at" AS t1_r3 FROM "sample_user_items" LEFT OUTER JOIN "sample_users" ON "sample_users"."id" = "sample_user_items"."sample_user_id" LIMIT ? [["LIMIT", 11]]
includes
結合先のテーブル (sample_users
) で、絞り込まない場合、preload と同様、クエリが分割して発行されます。
irb(main):016:0> SampleUserItem.includes(:sample_user).where(sample_user: 1)
SampleUserItem Load (0.2ms) SELECT "sample_user_items".* FROM "sample_user_items" WHERE "sample_user_items"."sample_user_id" = ? LIMIT ? [["sample_user_id", 1], ["LIMIT", 11]]
SampleUser Load (0.1ms) SELECT "sample_users".* FROM "sample_users" WHERE "sample_users"."id" = ? [["id", 1]]
結合先のテーブル (sample_users
) で、絞り込まない場合、eager_load と同様、LEFT OUTER JOIN が使われます。
irb(main):018:0> SampleUserItem.includes(:sample_user).where(sample_users: { id: 1 })
SQL (0.3ms) SELECT "sample_user_items"."id" AS t0_r0, "sample_user_items"."sample_user_id" AS t0_r1, "sample_user_items"."price" AS t0_r2, "sample_user_items"."name" AS t0_r3, "sample_user_items"."created_at" AS t0_r4, "sample_user_items"."updated_at" AS t0_r5, "sample_users"."id" AS t1_r0, "sample_users"."name" AS t1_r1, "sample_users"."created_at" AS t1_r2, "sample_users"."updated_at" AS t1_r3 FROM "sample_user_items" LEFT OUTER JOIN "sample_users" ON "sample_users"."id" = "sample_user_items"."sample_user_id" WHERE "sample_users"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 11]]
preload
クエリが分割して発行されます。
irb(main):010:0> SampleUserItem.preload(:sample_user)
(0.1ms) SELECT sqlite_version(*)
SampleUserItem Load (0.1ms) SELECT "sample_user_items".* FROM "sample_user_items" LIMIT ? [["LIMIT", 11]]
SampleUser Load (0.1ms) SELECT "sample_users".* FROM "sample_users" WHERE "sample_users"."id" = ? [["id", 1]]
おわりに
includes は絞り込みの条件次第で、LEFT OUT JOIN したり、preload したりと挙動が変わるので、
個人的には使わない方がいいかなという気持ちになりました。