【问题标题】:SQL: Get a selected row index from a querySQL:从查询中获取选定的行索引
【发布时间】:2010-07-19 14:21:32
【问题描述】:

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

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 字段对它们进行排序,并使用 ruby​​ 代码获取玩家的参与指数:

class Participation < ActiveRecord::Base

  belongs_to :player
  belongs_to :tournament

  def rank
    tournament.participations.index(self)
  end
end

参与的方法rank 获取其父锦标赛,加载所有锦标赛参与者的参与(由rating desc 排序)并在此集合中获取自己的索引

然后是这样的:

player.participations.last.rank

我不喜欢的一件事 - 它需要加载锦标赛的所有参与,如果我需要最近 10 场锦标赛的玩家排名,它会加载超过 5.000 个项目(并且随着新玩家的添加,它的数量会增加)。

我相信应该有使用 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 来说非常复杂)。

附:我正在使用 rails3.0.0.beta4

UPD:

这是获取玩家最后 10 个等级的最终 sql 请求(此外它还加载参与的锦标赛)

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;

【问题讨论】:

    标签: sql ruby-on-rails activerecord


    【解决方案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
    

    【讨论】:

    • Y,":players" 有错别字,谢谢。还添加了更多细节。
    • 'tour' 和 'tournament' 是一回事吗?您似乎可以互换使用它们。
    • 编辑了我的原始答案,因为将代码粘贴到 cmets 中并不起作用
    • 没错,巡回赛和锦标赛是一回事。也非常感谢您对 count 的建议 - 它对找到最终请求很有帮助!
    【解决方案2】:
    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;
    

    【讨论】:

    • 谢谢,对我很有用:)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-06-12
    • 1970-01-01
    • 1970-01-01
    • 2019-07-11
    • 1970-01-01
    • 2023-04-07
    • 1970-01-01
    相关资源
    最近更新 更多