2020-04-11

【Rails】ActiveRecordでJOIN先のテーブルのカラムで絞り込む

#includes#joins でテーブルをJOINしたときに、軸となるテーブルではなくJOIN先のテーブルのカラムで絞り込む方法について。

例として、デッキとカードという2つのモデルがありデッキがカードをN個持つ関係にあるとする。

app/models/deck.rb

class Deck < ApplicationRecord
  has_many :cards
end

app/models/card.rb

class Card < ApplicationRecord
  belongs_to :deck
end

※ 検証環境は以下の通り

  • rails 6.0.1
  • ruby 2.6.5

joins × merge

#joins#merge を組み合わせる。#includes の場合は使えない。

Deck.joins(:cards).merge(Card.where(id: 1))
# => SELECT "decks".* FROM "decks" INNER JOIN "cards" ON "cards"."deck_id" = "decks"."id" WHERE "cards"."id" = $1  [["id", 1]]

whereメソッドにテーブル名をkeyとして渡す

#joins の場合はINNER JOINとなり、#includes の場合はLEFT OUTER JOINになる。

Deck.joins(:cards).where(cards: { id: 1 })
# => SELECT "decks".* FROM "decks" INNER JOIN "cards" ON "cards"."deck_id" = "decks"."id" WHERE "cards"."id" = $1  [["id", 1]]

Deck.includes(:cards).where(cards: { id: 1 })
# => SELECT "decks"."id" AS t0_r0, ...(省略) FROM "decks" LEFT OUTER JOIN "cards" ON "cards"."deck_id" = "decks"."id" WHERE "cards"."id" = $1  [["id", 1]]

文字列でJOIN先のテーブルを指定する

Deck.joins(:cards).where('cards.id = ?', 1)
# => SELECT "decks".* FROM "decks" INNER JOIN "cards" ON "cards"."deck_id" = "decks"."id" WHERE (cards.id = 1)

#includes でこの書き方をする場合は #references と組み合わせないと動かない

Deck.includes(:cards).where('cards.id = ?', 1).references(:cards)
# => SELECT "decks"."id" AS t0_r0, ...(省略) FROM "decks" LEFT OUTER JOIN "cards" ON "cards"."deck_id" = "decks"."id" WHERE (cards.id = 1)