0

我仍然每天都在学习 ruby​​、rails 和 ActiveRecord。现在我正在通过我正在构建的一个新的小应用程序学习 SQL,但问题是我的应用程序的主视图目前每次页面刷新大约 2000 次查询,oouuuppps。

所以现在我知道我的数据库中有所有必需的信息并且我可以正确显示它们,现在是我优化它们的时候了,但我只是不知道从哪里开始说实话。

这些是我的模特协会

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_byjoinssum我无法让它工作。

我最接近工作的是这个

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

然后我可以通过做查询,players[player.id]但这并没有给我正确的结果,因为它没有考虑到 Gameweeks > Players > Player_fixtures 关系。

如何减少我正在执行的查询数量?我在freenode上继续#RubyOnRails,人们告诉我它可以在1个查询中完成,但不会向我指出任何方向或帮助我......

谢谢

4

2 回答 2

0

在您的position_based_leaderboard_statsN+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语句中,但我无法理解这些关联的依赖关系,抱歉。

于 2013-09-04T08:56:14.157 回答
0

花一些时间使用 SQL。终于找到了可以帮助我的查询。我还发现了 SQL 视图以及如何通过非常简洁的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
于 2013-09-04T14:25:19.963 回答