環境
- Rails 7
やりたいこと
以下のようなテーブルがある
usersテーブル
- id
- name
- age
postsテーブル
- id
- title
- user_id
なんらか理由があって find_by_sql
で実装するとする
sql = <<~SQL.squish SELECT posts.title, users.name FROM posts JOIN users ON posts.user_id = users.id SQL posts = Post.find_by_sql(sql)
がこれだとN+1問題が発生する。具体的には、各postに対してユーザー情報を取得するために、ユーザーテーブルに対するクエリがpostの数だけ発行される。これはデータベースへのアクセス回数が増え、パフォーマンスに影響を及ぼす。
解決策
ActiveRecord::Associations::Preloaderを使う。これはRailsが提供する機能で、指定した関連付けを事前にロードすることでN+1問題を解決する。
posts = Post.find_by_sql(sql) ActiveRecord::Associations::Preloader.new(records: posts, associations: :user).call
上記のコードでは、find_by_sqlで取得したpostsに対して、userの関連付けを事前にロードしている。これにより、各postのuserを参照する際に新たなクエリが発行されることなく、結果を取得できる。
posts = Post.find_by_sql(sql) ActiveRecord::Associations::Preloader.new.preload(posts, :user)