【问题标题】:MySQL row_number in group Man-Of-The-Match medalsMySQL row_number 在组 Man-Of-The-Match 奖牌中
【发布时间】:2014-11-10 17:43:38
【问题描述】:

我们的足球队有每场比赛的“最佳球员”投票结果。 现在我想要以金、银、铜为背景的 TOP 3 投票(或 TOP 4..5,当票数相等时)。 我更喜欢使用 SQL 查询而不是 php 代码来定义奖牌。

我的网站查询现在返回:

Game - Player - Points
Arsenal - Bart - 25
Arsenal - Joris - 17
Arsenal - Tim - 16
Arsenal - Tom - 16
Arsenal - Victor - 7
Arsenal - Jo - 1
Chelsea - Tim - 15
Chelsea - Bart - 15
Chelsea - Tom - 13
Chelsea - Ken - 10
Chelsea - Victor - 7
Chelsea  - Edris - 1

我无法得到这个结果:

Game - Player - Points - Rank
Arsenal - Bart - 25 - 1
Arsenal - Joris - 17 - 2
Arsenal - Tim - 16 - 3 
Arsenal - Tom - 16 - 3
Arsenal - Victor - 7 - 4
Arsenal - Jo - 1 - 5
Chelsea - Tim - 15 - 1 
Chelsea - Bart - 15 - 1
Chelsea - Tom - 13 - 2
Chelsea - Ken - 10 - 3
Chelsea - Victor - 7 - 4
Chelsea  - Edris - 1 - 5

这样会更好(会很完美,但我想会更难)

Game - Player - Points - Rank
Arsenal - Bart - 25 - 1
Arsenal - Joris - 17 - 2
Arsenal - Tim - 16 - 3 
Arsenal - Tom - 16 - 3
Arsenal - Victor - 7 - 5
Arsenal - Jo - 1 - 6
Chelsea - Tim - 15 - 1
Chelsea - Bart - 15 - 1
Chelsea - Tom - 13 - 3
Chelsea - Ken - 10 - 4
Chelsea - Victor - 7 - 5
Chelsea  - Edris - 1 - 6

有什么建议吗? 就是 tbl_MOTM (Id, Game, Player, Points)

【问题讨论】:

  • 这在 MySQL 中并不像在其他 dbms 中那么容易,因为缺少分析功能。为什么你还需要这个?你不使用编程语言来显示结果吗? Java,PHP,什么?然后用它为第一行着色。在我看来,不需要排名数字。
  • 这是一个“top n per group”问题,在mysql中不容易做到。
  • 我习惯使用 Microsoft MSSQL 和视图/存储过程。我需要排名,所以我可以创建一个 tbl_RankColorCode 来定义 php 网站中记录的背景。但是在 Mysql 中这似乎很难,所以我想我确实需要编写 php 循环来在那里定义 Rankcolors。但我会先尝试下面的解决方案。谢谢。

标签: mysql greatest-n-per-group partition row-number


【解决方案1】:

也许一些用户变量的小技巧会有所帮助。

首先,您需要定义要排序的数据集。假设您已经有一张这样的表格:

Game | Player | Points

所以你的查询应该是这样的:

select game, player, points
from your_table
-- Where conditions go here
order by points desc

所以,现在,让我们使用一个变量来做你需要的事情:

select a.game, a.player
     , @rank := @rank + (case when a.points != @points then 1 else 0 end) as rank
     , @points := a.points as points
from (select @rank := 0, @points := 0) as init
   , your_table as a
-- Where conditions go here
order by a.points desc

如果你想过滤这个以获得所有排名 5 或以上的玩家(并且只有 5 行或更少的行):

select b.*
from (
    select a.game, a.player
         , @rank := @rank + (case when a.points != @points then 1 else 0 end) as rank
         , @points := a.points as points
    from (select @rank := 0, @points := 0) as init
       , your_table as a
    -- Where conditions go here
    order by a.points desc
) as b
where rank <= 5
order by rank
limit 5

当然,通过巧妙地使用case...end,您可以定义任何条件来重置排名。如果您想为每个游戏重置@rank(注意:您必须将列按特定顺序放置才能正常工作;请记住,使用用户变量的 sql 语句是从左到右和从上到下计算的):

select a.player
     , @rank := (case 
                    when a.game != @game then 1
                    when a.points != @points then @rank
                    else @rank + 1
                end) as rank
     , @game := a.game as game
     , @points := a.points as points
from
    (select @game := ''
          , @points := 0
          , @rank := 0
    ) as init,
    your_table as a
-- Where conditions go here
order by a.game, a.points desc

如果您需要特定顺序的列,请将上述查询包含在子查询中,或者创建一个临时表(我更喜欢这个):

drop table if exists temp_result;
create temporary table temp_result
select a.player
     , @rank := (case 
                    when a.game != @game then 1
                    when a.points != @points then @rank
                    else @rank + 1
                end) as rank
     , @game := a.game as game
     , @points := a.points as points
from
    (select @game := ''
          , @points := 0
          , @rank := 0
    ) as init,
    your_table as a
-- Where conditions go here
order by a.game, a.points desc;
alter table temp_result
    add index idx_player(player),
    add index idx_game(game),
    add index idx_rank(rank);
-- Select top-5 players for each game:
select game, player, points, rank
from temp_result
where rank <= 5
order by game, rank;

记住:临时表只对创建它的连接可见,并且在创建它们的连接关闭或终止后被删除。

【讨论】:

  • 谢谢,我会测试一下。例如,您说“从 your_table 中选择游戏、玩家、积分,按积分顺序排列”,但应该是“按游戏顺序排列,按积分顺序排列”。所以这让事情变得更难了?
  • @Joris 没有必要......这只是在 case...end 中小心定义“重置”条件的问题
  • @Joris 检查我的编辑...我认为它回答了你的问题
【解决方案2】:

@巴兰卡:

非常感谢,我几乎可以复制粘贴您的代码,这里需要进行一些小修改:

select a.player
     , @rank := (case 
                    when a.game != @game then 1
                    when a.points = @points then @rank -- EDITED != to =
                    else @rank + 1
                end) as rank
     , @game := a.game as game
     , @points := a.points as points
from
    (select @game := ''
          , @points := 0
          , @rank := 0
    ) as init,
    your_table as a
-- Where conditions go here
order by a.game, a.points desc

这将给出我第一个问题中预期的结果,当有 3 个人具有 nr1 分数时,它不会将排名从 1 跳到 4。但这是我自己可以通过一些额外的努力来完成的事情。非常感谢!

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-07-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-12-19
    • 2022-12-01
    • 2022-12-02
    • 2022-12-26
    相关资源
    最近更新 更多