👏

joinとeager_loadとpreloadの違い

2021/08/01に公開

対象者

・rails 1,2年目の弱小エンジニア
・N + 1問題にとりあえずincludeで対処している人

概要

railsのテーブル結合するメソッドに主に上記の3つがあります。
そいつらの使い分けを、初級者に分かりやすく説明しようと思います。

また後半では、preloadとeager_loadの処理の概要を説明しようとしてます。

結論

# こんな感じで使います
User.join(:articles).where(articles: {is_published: true})
User.preload(articles: :comments, :company).each do { |user| user.articles.title }
User.eager_load(articles: :comments :company).where(articles: {is_published: true})
No メソッド SQL キャッシュ
1 join join ✖︎
2 preload select
3 eager_load left_join
  1. join
    テーブルをくっつけるけど、キャッシュはしません。
    その結果、絞り込みとかその場ではできるけど後で関連テーブルのデータを取ってこれません。
    また、joinなのでarticleを持っていないUserは取れません。
    これを利用して絞り込みとかにもよく使われます。

  2. preload
    テーブルをくっつけません。キャッシュはします。
    クエリがテーブルの数発行されてテーブル結合されません。
    あくまでselectで関連テーブルを1つづつ別々に取り出します。
    なので、whereによる絞り込みができません

  3. eager_load
    こいつが最強です。テーブルくっつけるし、キャッシュもします。
    ただ、多対多のテーブル読み込むとめっちゃ重くなったりします。

preloadとeager_loadのイメージ

user10人がartcile10記事で各々に10個コメントがあるとしましょう。
つまり各テーブルのレコード数は以下のようになります。
user 10人
article 100件
comment 1000件

preload

連結されていないテーブルが複数読み込まれます。(テーブル数分の3回SQLが発行される)
つまり、テーブルの横幅が狭くなって比較的軽いです。

user(10レコード)

id name gender
1 araki male
2 yamato male

article(100レコード)

id title content user_id
1 ホゲホゲ
2 睡眠 フガフガ 2

comment(1000レコード)

id content article_id
1 素晴らしい
2 zzz 1

eager_load

全てのテーブルを結合して、それを1000レコード分繰り返します。
めっちゃ重いけど、その代わり絞り込みとかはしやすいです。

user-articles-commentsの結合テーブル(1000レコード)

id title content user_id id name gender id content article_id
1 araki male 1 ホゲホゲ 1 1 素晴らしい
2 yamato male 2 睡眠 フガフガ 2 2 zzz 1

終わり

zenn初投稿でした。
稚拙な文章をここまで読んでいただきありがとうございます!
間違いなどがありましたら、ご指摘いただけるとありがたいです!

Discussion