SQL:从查询中获取选定的行索引

时间:2010-07-19 14:21:32

标签: sql ruby-on-rails activerecord

我有一个应用程序可以存储每个锦标赛的玩家评分。所以我有多对多的关联:

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;

2 个答案:

答案 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;