6

Mam trudności ze zdobyciem listy gier uczestniczących w hierarchicznym związku macierzystym, gdy zaimplementowanych jest wiele kluczy obcych w relacji środek.Jak uzyskać listę ze złożonego aktywnego rekordu, którego wiele dotyczyło z podzbioru dalszych obiektów?

Biorąc League Object NFC, znaleźć wszystkie jego grze obiektów [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 

Przykłady danych:

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) 

Odpowiedź będzie zawierać odpowiedzi zgodnej Szyny 4. Szczególną uwagę można odnieść do odpowiedzi w systemie RAILS 5, jeśli alternatywa Rails 4 jest bardzo nieefektywna.

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

Wyzwaniem IM, który jest z home_team/away_team i łączenie danych z klawiszy zagranicznych.

+0

Gorąco zachęcam do wyjaśnienia pytania. Wydaje się, że pytasz o dwie osobne rzeczy: "Jak uzyskać listę lig zaangażowanych w podzbiór gier" i jak "Znajdź wszystkie gry z zespołami NFC". Nie wyjaśnisz, jakie kryteria tworzy Zespół "NFC", a zamiast tego pokazujesz przykład sprawdzania dla Lig nazwanych "NFC". – coreyward

+0

Mam nadzieję, że zostało to wyjaśnione. – shadowbq

+0

Łatwiej byłoby po prostu wymienić gry zespołu w jednym skojarzeniu, a następnie "home" jako wartość boolowską. Tj .: Zespół 'has_many: games' - Home games to' games.where (home: true) ' –

Odpowiedz

2

zamierzam dać odpowiedź, ponieważ pierwsze rozwiązanie przez @meagar wymaga dwóch zapytań SQL zamiast jednego (też nie jest to, że Synt SQL błąd topora, jeśli liga nie ma drużyn?), , a drugie rozwiązanie będzie zawierało zduplikowane instancje gier , jeśli oba zespoły były z tej samej ligi.

Generalnie staram się unikać łączenia w moich wielokrotnych zakresach, , ponieważ wymuszają one zapytanie w pewnym "kształcie". Więc chciałbym napisać coś takiego:

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 

Ta technika SQL jest nazywany „skorelowane sub-query” na drodze. Przyznaję, że za pierwszym razem wygląda to dziwnie, ale jest to całkiem normalne. Możesz zobaczyć, że podzapytanie "dociera" do odwołania games. Twoja baza danych nie powinna mieć problemu z optymalizacją (oczywiście biorąc pod uwagę indeksy kluczy zagranicznych), ale mówiąc pojęciowo, uruchamia podzapytanie raz w rzędzie w tabeli games.

+0

ocenianie tego rozwiązania .. – shadowbq

+0

Byłoby to lepsze rozwiązanie, gdyby nie wymagało tabeli i nazw atrybutów do literału SQL. Arel umożliwia tworzenie bardziej elastycznych zakresów, które można ponownie wykorzystać bez kruchego, twardego kodowania atrybutów. – coreyward

3

Możliwym rozwiązaniem jest określenie sposobu games na League że znajdzie wszystkich gier, w których zarówno zagranicznych kluczowych punktów do jednego ze swoich zespołów:

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 

można osiągnąć to samo z 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

Czy możesz przepisać to jako rakieta lambda zakresu – shadowbq

+0

Możesz nagrodzić siebie lub anulować nagrodę .. Jeśli nie możesz i musisz czekać na to, aby wygasł, wrócę za 7 dni i spróbuję wymienić nagrodę i nagrodzić ją do Ciebie. (Nie za dużo expu z nagrodami za bounty) – shadowbq

+0

@shadowbq Naprawdę zachęcam cię do obejrzenia [Paul's answer] (http://stackoverflow.com/a/41885374/229044). Nie jestem wystarczającym guru SQL, aby ocenić jego wydajność w stosunku do mojego, ale na pewno jest poprawny, ponieważ moje kopie mogą zwrócić duplikaty drużyn. – meagar