joinsメソッドについて
前提条件
モデル
User has many pets のアソシエーションを行っています。
class User < ApplicationRecord has_many :pets # <= 関連名 en class Pet < ApplicationRecord belongs_to :user # <= 関連名 end
データ
usersテーブル
petsテーブル
joinsメソッドを使用してみる
User.joins(:pets)
実行すると以下のSQLが発行されます
SELECT "users".* FROM "users" INNER JOIN "pets" ON "pets"."user_id" = "users"."id"
joinsはデフォルトで内部結合(INNER JOIN)を行います。
又、SQLからusersテーブルからpetsテーブルのuser_id
カラムとusersテーブルのid
カラムがマッチしているレコードのみを取得しているのが分かります。
結果がこちらです。
結論
基本形
モデル名.joins(:関連名)
取得するのはモデルのレコードのみ
。=> 結合先のレコードを取得する為のメソッドではない!!
重複データを削除する(distinct)
先程の結果を見ると、たなか
とおぎ
というレコードが重複して発生しています。
この際、重複してレコードを削除してくれるのがdistinct
メソッドです。
User.joins(:pets).distinct
実行されるSQLが
SELECT DISTINCT "users".* FROM "users" INNER JOIN "pets" ON "pets"."user_id" = "users"."id"
SELECTの後にDISTINCT
が追加されているのが分かります。
結果がこちらです。
先程と違い、重複したレコードがなくなっているのが分かります。
selectメソッドを使用して取得するカラムを変更する。
参照先(ここではpets)のカラムを取得したい場合はselect
を使用します。
例えばpetsテーブルのspecies
も取得したい場合は
User.joins(:pets).select('users.*, pets.species')
発行されるSQLは
SELECT users.*, pets.species FROM "users" INNER JOIN "pets" ON "pets"."user_id" = "users"."id"
結果はこちら
ちゃんと追加されているのが分かります。
結論
selectを使用すれば、参照先のレコードも取得できる!!
しかし、キャッシュしないので結合先のレコードを取得するのには向いていない
whereを使用して条件を設定する
まずは例として以下を実行してみます。
User.joins(:pets).where(id:1)
発行されるSQLは
SELECT "users".* FROM "users" INNER JOIN "pets" ON "pets"."user_id" = "users"."id" WHERE "users"."id" = 1
where(id:1)
は、SQLではWHERE "users"."id" = 1
を指しています。
このことから、whereメソッドはusers
テーブルの条件を指していることが分かります。
結果はこちらです。
pets
のテーブルの条件を指定するには以下のように行います。
User.joins(:pets).where(pets: {id:1})
発行されるSQLは
SELECT "users".* FROM "users" INNER JOIN "pets" ON "pets"."user_id" = "users"."id" WHERE "pets"."id" = 1
where(pets: {id:1})
が、SQLではWHERE "pets"."id" = 1
を指しているのが分かります。
結果はこちらです。
結論
- 結合元の条件を設定する場合
モデル名.joins(:関連名).where(カラム名: 値)
- 結合先の条件を設定する場合
モデル名.joins(:関連名).where(テーブル名: { カラム名: 値 } )