LoginSignup
5
1

More than 1 year has passed since last update.

【ActiveRecord】JOIN したテーブルに条件を指定するときは merge を使おう【Tips】

Posted at

例として次のようなクラスがあると想定します。

class User < ApplicationRecord
  has_many :comments
end

class Article < ApplicationRecord
  belongs_to :user
  has_many :comments
end

class Comment < ApplicationRecord
  belongs_to :user
  belongs_to :article
end

item_type"Article"Comment を持つユーザーを取得したい場合、 joinswhere を使うと以下のようになります。

User.joins(:comments).where(comments: { item_type: "Article" })

これを merge を使って書き換えると次のようになります。

User.joins(:comments).merge(Comment.where(item_type: "Article"))

試しに、出力されるSQLを確認してみると、同じSQLが実行されることがわかります。

[17] (nobody)@development pry(main)> ::User.joins(:comments).where(comments: { item_type: "Article" }).to_sql
=> "SELECT `users`.* FROM `users` INNER JOIN `comments` ON `comments`.`user_id` = `users`.`id` WHERE `comments`.`item_type` = 'Article'"
[18] (nobody)@development pry(main)> ::User.joins(:comments).merge(::Comment.where(item_type: "Article")).to_sql
=> "SELECT `users`.* FROM `users` INNER JOIN `comments` ON `comments`.`user_id` = `users`.`id` WHERE `comments`.`item_type` = 'Article'"

上記の例ではあまりメリットが感じられませんが、以下のように複数 join する場合、 merge を使った書き方の方がわかりやすく短いコードで済むことが多いので、このテクニックを覚えておくことをオススメします!

# merge を使わない場合
User.joins(comments: { article: :tags }).where(comments: { article: { tags: { name: "Haskell" }}})
# merge を使った場合
User.joins(comments: { article: :tags }).merge(Tag.where(name: "Haskell"))
5
1
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
5
1