Railsで子テーブルのレコードでwhereメソッドを使って条件検索する方法

たとえば、created_atが一週間以内のusersテーブルのレコードの中で、子テーブルであるpostsテーブルのレコードのcreated_atが三日以内のものを取得したい時、

arr = []
Post.where('created_at >= ?', Time.current - 3.day).each do |post|
  if post.user.created_at >= Time.current - 1.week
    arr.push(post.user)
  end
end

のようにすれば取得できるけれど、冗長だしパフォーマンスも悪い。

そこでテーブル結合を使えば、可読性もパフォーマンスも高くできる。

テーブル結合のやり方

joinsメソッドを使って行う。

User.joins(:posts)

のように記述する。

これで、postsレコードのuser_idがusersレコードのidと一致する、usersレコードを取得できる。つまり、postしたuserのみを取得している。

User.all.count

106

User.joins(:posts).count

68

テーブル結合後にwhereで条件検索

joinsした後に、whereでレコードを取得したい時、以下のようにすればいい。

User.joins(:posts).where('users.created_at >= ?', Time.current - 1.week).count

20

ポイントは、テーブル結合した後で条件文を書く場合、カラム名の前にテーブル名をドットでつなげて明示すること。where(‘[テーブル名].[カラム名] [演算子] ?’, [値])の形式を使う。

User.joins(:posts).where('users.created_at >= ?', Time.current - 1.week).where('posts.created_at >= ?', Time.current - 3.day)

これで、created_atが一週間以内のusersテーブルのレコードの中で、子テーブルであるpostsテーブルのレコードのcreated_atが三日以内のものを取得できた。

一致で検索する場合は、以下のような書き方もある。

User.joins(:posts).where(posts: {status_id: "enable"})

これでpostsテーブルのstatus_idカラムがenableのレコードを取得することができる。