6

Мне сложно получить список игр, связанных с иерархической родительской связью, когда несколько внешних ключей реализованы в отношении в середина.Как получить список из сложной активной записи has_many, участвующей в подмножестве нисходящих объектов

Учитывая лиги объекта 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 5, если альтернатива Rails 4 очень неэффективна.

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

Задача Im имея с является home_team/away_team и объединение данных из внешних ключей.

+0

Я настоятельно рекомендую вам уточнить свой вопрос. Кажется, вы задаете две разные вещи: как «получить список лиг, участвующих в подмножестве игр», и как «найти все игры, в которых есть команды NFC». Вы не объясняете, какие критерии составляют команду «NFC», а вместо этого показывают пример запросов к лигам с именем «NFC». – coreyward

+0

Надеюсь, его разъяснили. – shadowbq

+0

Было бы намного проще просто перечислить игры команды в одной ассоциации, а затем указать «home» как логическое значение. Т.е.: Команда 'has_many: games' - Домашние игры:' games.where (home: true) ' –

ответ

2

Я собираюсь дать ответ, , потому что первое решение по @meagar требует двух SQL запросов вместо одного (также, не то, что SQL Synt если в лиге нет команд?), , а второе решение будет содержать повторяющиеся экземпляры игры , если обе команды были из той же лиги.

В общем, я стараюсь избегать объединений в своих многоразовых областях, , поскольку они заставляют запрос в определенной «форме». Так что я бы написать что-то вроде этого:

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.

+0

, оценивая это решение. – shadowbq

+0

Это было бы лучшим решением, если бы оно не требовало бы таблиц жесткого кодирования и имен атрибутов в литерал SQL. Arel позволяет создавать более гибкие области, которые можно повторно использовать без хрупкого жесткого кодирования атрибутов. – coreyward

3

Возможное решение состоит в определении метода games на League, который находит все игры, где либо внешний ключ указывает на одну из своих команд:

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) 
+0

вы можете переписать это как раму лямбда-сферы – shadowbq

+0

Можете ли вы присудить себе или отменить награду. Если вы не можете и не должны ждать, чтобы она закончилась, я вернусь через 7 дней и попытаюсь заменить награду и награду ее тебе. (Не много из exp с щедростями извините) – shadowbq

+0

@shadowbq Я действительно призываю вас взглянуть на [ответ Павла] (http://stackoverflow.com/a/41885374/229044). Мне не хватает гуру SQL, чтобы оценить его производительность по сравнению с моей, но он определенно прав в том, что моя может вернуть дубликаты команд. – meagar