2016-10-11

ActiveRecordのpreload, includes, eager_load

よくあるpreload, includes, eager_loadの違いについて実際に打ってみて確認してみた。今回はuserレコードがtaskレコードに対して1対多のERにおいてuserとそれに紐づくtaskを取得するようなクエリの挙動を確認します。

クエリ2回に分けられるパターン

User.includes(:tasks)
User.preload(:tasks)

実際のクエリはこんな感じでusersのID配列でtasksを検索している

User Load (3.6ms) SELECT "users".* FROM "users"
Task Load (0.7ms) SELECT "tasks".* FROM "tasks" 
  WHERE "tasks"."user_id" IN (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16)

JOINしてくれるパターン

User.eager_load(:tasks)
User.includes(:tasks).references(:tasks)
User.includes(:tasks).where(tasks: { completed: false })

SELECT句がワイルドカード記述ではなくなり、LEFT OUTER JOINしてくれます。

SELECT "users"."id", ..., "tasks"."id", ... FROM "users" 
  LEFT OUTER JOIN "tasks" ON "tasks"."user_id" = "users"."id"

WHERE句にカラムねーよ、って言われるパターン

以下のようにincludesを使ったJOINでwhere句にハッシュを使わない場合に、includesしたテーブルのカラムを条件にするとエラーになります。

User.includes(:tasks).where("tasks.completed = false")
SQLite3::SQLException: no such column: tasks.completed: 
SELECT "users".* FROM "users" WHERE (tasks.completed = false)

where句がハッシュの場合は色々と空気を読んでLEFT OUTER JOINしてくれるけど、文字列はWHERE句にそのまま入れるだけっぽいです。クエリ2回パターンと同様に、usersテーブルを取得しようとした際にtasksのカラムで絞込をしようとして、エラーになっています。

参考URL

ActiveRecordのjoinsとpreloadとincludesとeager_loadの違い - Qiita

このエントリーをはてなブックマークに追加