【问题标题】:How do you get a list from complex active record has_many involved from a subset of downstream objects如何从下游对象子集中涉及的复杂活动记录 has_many 中获取列表
【发布时间】:2017-06-09 12:33:47
【问题描述】:

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

给定联赛对象NFC,找到它的所有游戏对象[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

数据示例:

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)

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

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

我面临的挑战是home_team / away_team 并结合来自外键的数据。

【问题讨论】:

  • 我强烈建议您澄清您的问题。您似乎问了两个不同的问题:如何“从部分游戏中获取涉及的联赛列表”以及如何“查找所有拥有 NFC 球队的游戏”。您没有解释什么标准使团队成为“NFC”,而是展示了一个查询名为“NFC”的联赛的示例。
  • 希望它得到澄清。
  • 在单个关联中列出一个团队的比赛会简单得多,然后将home 指示为布尔值。即:Team has_many :games -- 主场比赛是games.where(home: true)
  • @meagar 他没有将“游戏”定义为团队关系,因此无法按原样工作。
  • @coreyward 你是对的,我的错。我在关闭之前移除了赏金,我无法恢复它,但我会提供我自己的赏金。

标签: ruby-on-rails ruby-on-rails-4 activerecord has-many-through


【解决方案1】:

一种可能的解决方案是在League 上定义一个games 方法,以查找其中任一外键指向其中一个团队的所有游戏:

class League

  has_many :teams


  def games
    Game.where('team_a_id in (:ids) or team_b_id in(:ids)', ids: teams.pluck(:id))
  end
end

您可以使用join 完成同样的事情:

Game.joins('inner join teams on teams.id = games.team_a_id or teams.id = games.team_b_id').where('teams.league_id = ?', id) 

【讨论】:

  • 你能把它改写成scope lambda rocket
  • 你能奖励自己还是取消赏金..如果你不能并且需要等待它失效,我会在7天后回来尝试更换赏金并奖励给你. (抱歉,赏金的经验不多)
  • @shadowbq 我真的鼓励你看看Paul's answer。我没有足够的 SQL 专家来衡量它相对于我的性能,但他绝对正确,因为我可能会返回重复的团队。
【解决方案2】:

我会给出一个答案, 因为@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

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

【讨论】:

  • 如果不需要将表和属性名称硬编码到 SQL 文字中,这将是一个更好的解决方案。 Arel 使创建更灵活的范围成为可能,这些范围可以重复使用,而无需对属性进行脆弱的硬编码。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-07-27
  • 2013-09-19
  • 2021-06-27
  • 1970-01-01
  • 2011-12-22
  • 2019-03-30
  • 2012-12-29
相关资源
最近更新 更多