はじめに
ActiveRecordとは、Ruby on Railsに標準で組み込まれている ORM(Object-Relational Mapping)ライブラリ。SQL文を直接記述することなく、DB操作をRubyのオブジェクト操作に変換して簡単に実現できる。
includes、preload、eager_loadどれもN+1問題を防ぐためのEager Loading(事前読み込み) です。「何がなに?」と思ったため、違いについてまとめていきます。
includes
includesメソッドは、条件によってpreload、eager_loadに自動で使い分けている。
preloadと同様
User.includes(:posts)
SELECT * FROM users;
SELECT * FROM posts WHERE user_id IN (...);
JOINを使わず、関連テーブルごとに独立したクエリを投げている
eager_loadと同様
User.includes(:posts).where(posts: { published: true })
SELECT * FROM users
LEFT OUTER JOIN posts ON ...
WHERE posts.published = true;
ActiveRecordが「JOIN必要そうだな」と判断すると自動で切り替えている
preloadはどこで使う?
preloadを使う時は、主に以下の二つ。
・ただ表示するだけ(一覧画面など)
・データ量が多いとき
例)
User.preload(posts: :comments)
段階的に3回クエリを投げる
SELECT "users".* FROM "users";
SELECT "posts".* FROM "posts"
WHERE "posts"."user_id" IN (1, 2, 3, ...);
SELECT "comments".* FROM "comments"
WHERE "comments"."post_id" IN (10, 11, 12, ...);
流れ的には、
users取得 →(user.idを集める)→ posts取得 →(post.idを集める)→ comments取得
件数が多すぎると、メモリを多く消費するので注意!!
eager_loadはどこで使う?
・SQLを明示的にコントロールしたい
・クエリを1回にまとめたい
User.eager_load(:posts).where(posts: { published: true })
JOINしていることを明確に示しているだけなので、
User.includes(:posts).where(posts: { published: true })
と同じ
参考記事
ActiveRecordのpreloadが親子関係をキャッシュする仕組みを調べてみた
似ているようで全然違う!?Activerecordにおけるincludesとjoinsの振る舞いまとめ
ActiveRecordのincludes、joins、eager_load等の違いを調べてみた