Railsで内部結合・外部結合する方法をメモとして残しておきます。
【Rails】内部結合・外部結合する方法
例として以下のモデルを使用します。会社(Company) から内部結合・外部結合します。
- 会社(Company) 1:N オフィス(Office)
- オフィス(Office) 1:N 従業員(Employer)
- 従業員(Employer) 1:N 備品(Equipment)
※リレーションは各モデルに記載する必要があります。
内部結合
joinsを使用することで内部結合します。
結合先のデータが不要な場合はjoinsだけでよいが、eager_loadと組み合わせることで結合先のモデルを一括で取得してくれます。
eager_loadをincludesに変更しても同じ挙動をします。
外部結合
eager_loadを使用することで外部結合します。eager_loadにより結合先の対象モデル(ここでいうOffice)を一括で取得するため、ループ時のN+1問題を解消することができます。
eager_loadデータ一括で取得しないと、結合先のモデルにアクセスする度にSQLが発行されるため、データ量が多いと落ちる可能性があります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | class Company < ApplicationRecord ## # リレーション ## has_many :offices ## # scopes ## # オフィス(外部結合) scope :with_offices, -> { eager_load(:offices) } # オフィス(内部結合) scope :with_inner_offices, -> { joins(:offices).eager_load(:offices) } end |
1 2 3 | # 使用例 query = ::Company.all query = query.with_inner_offices |
孫テーブルまで結合したい場合は以下の通りです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | class Company < ApplicationRecord ## # リレーション ## has_many :offices ## # scopes ## # オフィス→従業員(外部結合) scope :with_employers, -> { eager_load(offices: :employers) } # オフィス→従業員(内部結合) scope :with_inner_employers, -> { joins(offices: :employers).eager_load(offices: :employers) } end |
ひ孫テーブルまで結合したい場合は以下の通りです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | class Company < ApplicationRecord ## # リレーション ## has_many :offices ## # scopes ## # オフィス→従業員→備品(外部結合) scope :with_equipments, -> { joins({:offices => {:employers => :equipments}}).eager_load({:offices => {:employers => :equipments}}) } # オフィス→従業員→備品(内部結合) scope :with_equipments, -> { joins({:offices => {:employers => :equipments}})} end |
参考: