2013-09-04 2 views
0

Я все еще изучаю рубин, рельсы и ActiveRecord каждый день. Сейчас я изучаю SQL через новое небольшое приложение, которое я создаю, но проблема в том, что основной вид моего приложения в настоящее время составляет ~ 2000 запросов на обновление страницы, oouuuppps.Как оптимизировать этот запрос ActiveRecord?

Итак, теперь, когда я знаю, что у меня есть вся необходимая информация в моей БД и что я могу отображать их правильно, мне пора оптимизировать их, но я просто не знаю, с чего начать честно.

Это мои модели объединения

class League < ActiveRecord::Base 
    belongs_to :user 
    has_many :league_teams 
    has_many :teams, :through => :league_teams 
end 


class Team < ActiveRecord::Base 
    has_many :gameweeks 
    has_many :league_teams 
    has_many :leagues, :through => :league_teams 
end 


class Gameweek < ActiveRecord::Base 
    belongs_to :team 
    has_and_belongs_to_many :players 
    has_and_belongs_to_many :substitutes, class_name: "Player", join_table: "gameweeks_substitutes" 
    belongs_to :captain, class_name: "Player" 
    belongs_to :vice_captain, class_name: "Player" 
end 


class Player < ActiveRecord::Base 
    serialize :event_explain 
    serialize :fixtures 
    serialize :fixture_history 
    has_many :gameweeks, class_name: "captain" 
    has_many :gameweeks, class_name: "vice_captain" 
    has_and_belongs_to_many :gameweeks 
    has_many :player_fixtures 
end 

Так что это мой контроллер:

@league = League.includes(teams: [{gameweeks: [{players: :player_fixtures} , :captain]}]).find_by(fpl_id:params[:fpl_id]) 
@teams = @league.teams 
@defense_widget_leaderboard = @league.position_based_leaderboard_stats(@teams, ['Defender', 'Goalkeeper']) 

И это одна из метода в моей модели Лиги:

def position_based_leaderboard_stats(teams,positions_array) 
    leaderboard = [] 

    teams.each do |team| 
     position_points = 0 
     gameweeks = team.gameweeks 

     gameweeks.each do |gameweek| 
     defense = gameweek.players.where(type_name:positions_array) 

     defense.each do |player| 
      player.player_fixtures.where(gw_number: gameweek.number).each do |p| 
      position_points += p.points 
      end 
     end 
     end 
     leaderboard << [team.team_name,position_points] 
    end 
    return leaderboard.sort_by {|team| team[1]}.reverse 
    end 

У меня есть 4 метода, которые выглядят более или менее то же, что и выше. Каждый из них выполняет от 300 до 600 запросов.

Насколько я понимаю, это типичный случай N + 1 запросов. Я попытался уменьшить с помощью включений в @league, но это заставило меня отказаться от 2000 до 1800 запросов.

Я просмотрел group_by, joins и sum, но я не смог заставить его работать.

Ближе всего я должен работать был этот

players = PlayerFixture.group("player_id").sum(:points) 

Где я мог бы запросить, делая players[player.id], но это не дает мне правильные результаты в любом случае, потому что он не принимает во внимание в Gameweeks> Игроки> Player_fixtures отношения.

Как уменьшить количество запросов, которые я выполняю? Я пошел на #RubyOnRails на Freenode и люди говорили мне, что это может быть сделано в 1 запрос, но не будет указывать мне в любых направлениях или помочь мне ...

Благодарности

ответ

0

Проведите некоторое время с помощью SQL. Наконец нашел запрос, который может помочь мне в этом. Я также обнаружил SQL Views и как use them through Activerecord, который довольно опрятен.

Окончательный успешный запрос

CREATE VIEW team_position_points AS 

select teams.id as team_id, teams.team_name, players.type_name, sum(points) as points 
from teams 
inner join gameweeks on teams.id = gameweeks.team_id  
inner join gameweeks_players on gameweeks.id = gameweeks_players.gameweek_id 
inner join players on gameweeks_players.player_id = players.id 
inner join player_fixtures on players.id = player_fixtures.player_id AND player_fixtures.gw_number = gameweeks.number 
group by teams.id, players.type_name 
0

В вашей задаче position_based_leaderboard_stats N + 1 появляется , слишком. Таким образом, вы можете предварительно загрузить все ваши ассоциации, прежде чем each циклов:

def position_based_leaderboard_stats(teams,positions_array) 
    leaderboard = [] 
    Team.preload(gameweeks: players).where('players.type_name=?', positions_array) 
    your code 

Кроме того, вы можете добавить к player_fixtures preload заявления, но я не могу понять зависимости от этих ассоциаций, извините.

+0

Только что попробовал, и это ничего не меняет. Метод все еще выполняет ~ 640 запросов. – Syl

+0

Я сделал опечатку: она должна быть (gameweeks:: players) вместо (gameweeks: players). Вы исправились? – hedgesky

+0

Да, я исправил вашу опечатку. Все еще ничего не меняет на количество запросов: / – Syl

Смежные вопросы