我有一个应用程序可以存储每个锦标赛的玩家评分。所以我有多对多的关联:
Tournament
has_many :participations, :order => 'rating desc'
has_many :players, :through => :participations
Participation
belongs_to :tournament
belongs_to :player
Player
has_many :participations
has_many :tournaments, :through => :participations
参与模型有一个rating
字段(浮动),可以存储每个锦标赛中每个玩家的评分值(就像得分点一样)。
我想要的东西 - 获得玩家的最后10个等级(rank
是特定锦标赛中玩家的位置,基于他的评分:评分越高 - 排名越高)。现在要获得一名球员在锦标赛中的排名,我将加载所有参赛资格,按rating
字段对其进行排序,并使用红宝石代码获取玩家参与指数:
class Participation < ActiveRecord::Base
belongs_to :player
belongs_to :tournament
def rank
tournament.participations.index(self)
end
end
参与的方法rank
获得其父级锦标赛,加载所有tournamentr的参与(按rating desc
排序)并在此集合中获取自己的索引
然后像:
player.participations.last.rank
我不喜欢的一件事 - 它需要加载锦标赛的所有参与,如果我需要最近10场比赛的球员排名,它会加载超过5000个项目(当新球员加入时其数量会增加)。
我认为应该有办法使用SQL。实际上我试图使用SQL变量:
find_by_sql("select @row:=@row+1 `rank`, p.* from participations p, (SELECT @row:=0) r where(p.tournament_id = #{tournament_id}) order by rating desc limit 10;")
此查询从给定的锦标赛中选择前10名。我一直试图修改它以选择给定用户及其排名的最后10个参与。
会感激任何帮助。 (我认为解决方案将是一个SQL请求,因为它对ActiveRecord来说非常复杂)。
P.S。我正在使用rails3.0.0.beta4
UPD:
这是最终的sql请求,它获取了玩家的最后10个等级(此外它还加载了参与的锦标赛)
SELECT *, (
SELECT COUNT(*) FROM participations AS p2
WHERE p2.tour_id = p1.tour_id AND p2.rating > p1.rating
) AS rank
FROM participations AS p1 LEFT JOIN tours ON tours.id = p1.tour_id WHERE p1.player_id = 68 ORDER BY tours.date desc LIMIT 10;
答案 0 :(得分:1)
首先,应该这样:
Participation
belongs_to :tournament
belongs_to :players
是吗?
Participation
belongs_to :tournament
belongs_to :player
即,在belongs_to之后的单数玩家?
我正在努力解决这个问题:
class Participation
def rank_at_tour(tour)
tour.participations.index(self)
end
end
您没有真正解释您的架构,以便轻松进行逆向工程。它是在做以下......?
“获取给定游览的所有参与并返回当前参与该列表的位置”?这是你如何计算排名?如果是这样,我同意这似乎是一种非常复杂的做法。
您是否为播放器获得的十个参与对象执行上述操作,然后取平均值?什么是评级?这与排名有什么关系吗?基本上,你能解释一下你的架构,然后重申你想做的事情吗?
修改
我认为你只需要一种更有效的方法来找到这个位置。有一种方法我可以想到我的头脑 - 获得你想要的记录,然后计算它上面有多少。加1,你得到的位置。例如
class Participation
def rank_at_tour(tour)
tour.participations.count("rating > ?", self.rating) + 1
end
end
您应该在日志文件中看到(例如,在控制台中进行实验时),这只会进行计数查询。如果你在评级字段上有一个索引(如果你没有这个索引),那么这将是一个非常快速的执行查询。
另外 - 如果巡回赛和锦标赛是相同的(就像我说你似乎可以互换地使用它们)那么你不需要将巡回赛传递给参赛者,因为它无论如何都属于巡回赛。只需将方法更改为排名:
class Participation
def rank
self.tour.participations.count("rating > ?", self.rating) + 1
end
end
答案 1 :(得分:1)
SELECT *, (
SELECT COUNT(*) FROM participations AS p2
WHERE p2.tour_id = p1.tour_id AND p2.rating > p1.rating
) AS rank
FROM participations AS p1 LEFT JOIN tours ON tours.id = p1.tour_id WHERE p1.player_id = 68 ORDER BY tours.date desc LIMIT 10;