我每天都在学习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_by
,joins
和sum
,但我无法使其发挥作用。
我最接近工作的是这个
players = PlayerFixture.group("player_id").sum(:points)
然后我可以通过players[player.id]
进行查询,但这并不能给我正确的结果,因为它没有考虑到Gameweeks&gt;玩家&gt; Player_fixtures关系。
如何减少我正在进行的查询次数?我在freenode上继续#RubyOnRails,人们告诉我它可以在一个查询中完成,但不会指向我任何方向或帮助我...
由于
答案 0 :(得分: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
语句,但我无法理解这些关联的依赖关系,抱歉。
答案 1 :(得分:0)
花一些时间在SQL上。终于找到了可以帮助我的查询。我还发现了SQL视图以及如何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