LoginSignup
69
72

More than 5 years have passed since last update.

eager_load, preload, includesの違い(自分用まとめ)

Last updated at Posted at 2015-11-02

発行されているSQLは下記を引用:
Rails - ActiveRecordのjoinsとpreloadとincludesとeager_loadの違い - Qiita

eager_load

User.eager_load(:posts)

SELECT `users`.`id` AS t0_r0,
       `users`.`name` AS t0_r1,
       `users`.`created_at` AS t0_r2,
       `users`.`updated_at` AS t0_r3,
       `posts`.`id` AS t1_r0,
       `posts`.`user_id` AS t1_r1,
       `posts`.`created_at` AS t1_r2,
       `posts`.`updated_at` AS t1_r3
FROM `users`
LEFT OUTER JOIN `posts` ON `posts`.`user_id` = `users`.`id`

User.eager_load(:posts).where(posts: { id: 1 })

SELECT `users`.`id` AS t0_r0,
       `users`.`name` AS t0_r1,
       `users`.`created_at` AS t0_r2,
       `users`.`updated_at` AS t0_r3,
       `posts`.`id` AS t1_r0,
       `posts`.`user_id` AS t1_r1,
       `posts`.`created_at` AS t1_r2,
       `posts`.`updated_at` AS t1_r3
FROM `users`
LEFT OUTER JOIN `posts` ON `posts`.`user_id` = `users`.`id`
WHERE `posts`.`id` = 1

テーブルを結合して1発でもってくる。
テーブルデータがでかくて結合するのがしんどいやつはeager_loadではしんどいと思われる。

さらに重要な点としては結合しているので、has_manyな関係と結合するとsum計算するときに結合してできたぶんのレコードを重複して足し合わせてしまう場合があるかもしれない。

belongs_toな関係を取得したい(親を取得したい)場合はeager_loadにするとSQLの発行回数がpreloadに比べて減らせてうれしいという発想もある。

preload

User.preload(:posts)

SELECT `users`.*
FROM `users`

SELECT `posts`.*
FROM `posts`
WHERE `posts`.`user_id` IN (1, 2, 3, ...)

User.preload(:posts).where(posts: { id: 1 })
preloadをwhereで絞り込むとRoRがダメ(できない)っていう。

SELECT `users`.*
FROM `users`
WHERE `posts`.`id` = 1 # => Mysql2::Error: UNKNOWN COLUMN 'posts.id' IN 'where clause': SELECT `users`.* FROM `users` WHERE `posts`.`id` = 1

さらに重要な点としてはeager_loadと違ってSQLは2回発行されるが、結合していないので、sum計算で結合によって生じた(そもそも結合しないからね)重複分を足し合わせてしまう心配がない。

has_manyな関係を取得したい(子を取得したい)場合はpreloadにするとeager_loadだと自分自身が重複してメモリにとられてしまうぶんのメモリを効率的につかえるという発想もある。

includes

空気を読んで、eager_loadの挙動かpreloadの挙動に変わる。
User.includes(:posts).where(posts: { id: 1 })
と書くと、preloadwhereで絞り込めないので、eager_loadの動きになる(空気読んでる)。

まとめ

テーブル結合してもメモリとか大丈夫そうだとeager_load。でも集計関数などで結合重複ぶんを数えてしまいそうなときは注意。

テーブル結合してメモリとかヤバそうだとSQLの発行回数増えるけど、preload。さらに集計関数などで重複ぶんを数えてしまうことがないので(結合しないからそもそも重複しない)扱いやすい。



追加でjoinsmergeも。

ここからはほぼ
Rails と テーブル結合 - Qiita
の該当部分の引用となります。

joins

eager_load, preload と違いキャッシュしない。
1回こっきり使用するクエリーの時に使用と思われる。

joinsinner joinを行うので、データはinner join的に捨てられる。
つまりデータのしぼりこみに使える。

merge

joins で結合したテーブルの条件式に、ActiveRecord::Relationを使用することができる。

User.joins(:avatar).merge(Avatar.where(id: 1))
=>
SELECT users.* FROM users INNER JOIN avatars ON avatars.resource_id = users.id AND avatars.resource_type = 'User' WHERE avatars.id = 1

# 普通に書いた場合
User.joins(:avatar).where('avatars.id = 1')
or
User.joins(:avatar).where(avatars: {id: 1})
69
72
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
69
72