如何从下游对象的子集中获取复杂活动记录has_many的列表

sha*_*wbq 6 activerecord ruby-on-rails has-many-through ruby-on-rails-4

当在中间的关系上实现多个外键时,我很难从分层父关系中获取所涉及的游戏列表.

鉴于League Object NFC,找到它的所有Game对象[G1,G3,G4]

#  id           :integer          not null, primary key
#  name         :string
class League
  has_many :teams
  # has_many :games, :through => :teams (Is there some way to do this?)
end

#  id         :integer          not null, primary key
#  team_name    :string
#  league_id :integer
class Team
  belongs_to :league
  has_many :home_games, :foreign_key => team_a_id, :source => :game
  has_many :away_games, :foreign_key => team_b_id, :source => :game
end

#  id                   :integer          not null, primary key
#  game_name            :string
#  team_a_id :integer          not null
#  team_b_id :integer          not null
class Game
  belongs_to :home_team, :class_name => Team
  belongs_to :away_team, :class_name => Team
end
Run Code Online (Sandbox Code Playgroud)

数据示例:

LEAGUE - TEAM - GAME 
---------------------------------
AFC - 
        PATRIOTS - 
                 Home       Away   
               G1(PATRIOTS vs DALLAS)
               G2(PATRIOTS vs PITTSBURG)
        PITTSBURG - 
               G2(PATRIOTS vs PITTSBURG)
NFC - 
        DALLAS - 
               G1(PATRIOTS vs DALLAS)
               G3(DALLAS vs GREENBAY)
               G4(DALLAS vs SEATTLE)
        GREENBAY
               G3(DALLAS vs GREENBAY)
        SEATTLE
               G4(DALLAS vs SEATTLE)
Run Code Online (Sandbox Code Playgroud)

答案将包含符合Rails 4的答案.如果Rails 4替代方案效率非常低,则可以特别考虑RAILS 5的答案.

nfc = League.where(name: 'NFC').first
# <answer>
puts nfc.games 
##  array containing objects [G1,G2,G3]
Run Code Online (Sandbox Code Playgroud)

我所遇到的挑战是来自外键的home_team/ away_team和组合数据.

Pau*_*rth 3

我要给出一个答案,因为 @meagar 的第一个解决方案需要两个 SQL 查询而不是一个(另外,如果联盟没有球队,这不是一个 SQL 语法错误吗?),第二个解决方案将包含重复的如果两支球队来自同一联赛,则比赛实例。

一般来说,我会尽量避免在可重用范围中进行连接,因为它们会强制查询达到某种“形状”。所以我会写这样的东西:

class Game
  # ...
  scope :for_league, ->(league_id) {
    where(<<-EOQ, league_id)
      EXISTS (SELECT  1
              FROM    teams t
              WHERE   t.id IN (games.team_a_id, games.team_b_id)
              AND     t.league_id = ?)
    EOQ
  }
  # ...
end
Run Code Online (Sandbox Code Playgroud)

顺便说一句,这种 SQL 技术称为“相关子查询”。我承认第一次看到它时看起来很奇怪,但这是很正常的事情。您可以看到子查询“伸出”到引用games。您的数据库优化它应该没有问题(当然给定外键上的索引),但从概念上讲,它在表中的每行运行一次子查询games