2

具有以下列的表格:

Player_id (primary key), Event_type(A,B,C), Points.

每个 event_type 可能有 1 名玩家出现多次

我想显示所有事件类型的 DESC SUM(Points) GROUP BY player_id 的总体排名,同时提出一些条件:

  • 对于事件类型 A,每个 player_id 只有最好的 5 个结果
  • 对于事件类型 B,每个 player_id 只有最好的 2 个结果
  • 对于事件类型 C,每个 player_id 只有最好的 3 个结果

我徒劳地尝试过:

SUM(points) WHERE event_type ="X" 
GROUP BY Player_id ORDER BY SUM(points) LIMIT N

一个星期以来,我一直在与这个头痛作斗争,当涉及到包含子查询、UNION 或临时表时,我感到非常困惑。我不知道如何将所有部分放在一起...

我的梦想是让这个整体排名运行起来,能够在点击时访问每个玩家的详细积分细分......

愿意为此提供任何帮助...谢谢!

源表示例:

player_id-----event_type-----score-----

---1--------------------A---------------- 5 --------- -

---1--------A--------------- 10 ---------

---1--------------------A---------------- 5 ---------

---1--------------------A----------------5---------

---1--------------------A----------------2---------

---1--------------------A---------------- 15 ---------

---1--------------------A---------------- 10 ---------

---1--------------------C----------------20---------

---1--------------------B---------------- 5 ---------

---1--------------------B----------------5---------

---1--------------------B---------------- 20 ---------

---2--------------------A---------------- 50 ---------

---2--------------------B---------------- 55 ---------

根据此示例的所需输出:

Rank---player_id-----overall_score-----

----1------------2----------- 105 分[A 50 分(最佳 5)+ B 55 分(最佳 2)]---- -----

----2------------1----------- 90 分[A 中 45 分(最佳 5)+ C 中 20 分(最佳 3)+ B 中 25 分(最佳2)]---------

4

2 回答 2

3

首先:您想要的功能称为sliding windowranking。Oracle 使用OVER-keyword 和rank()-function 来实现这些。MySQL 不支持这些特性,所以我们必须解决这个问题。

我使用这个答案创建了以下查询。+1如果这对你有帮助,也给他一个。

SELECT 
    `player_id`, `event`, `points`,
    (SELECT 1 + count(*) 
     FROM `points` 
     WHERE `l`.`player_id` = `player_id` 
         AND `l`.`event` = `event` 
         AND `points` > `l`.`points`
    ) AS `rank`
FROM
    `points` `l`

这将输出每个player_idevent的等级points。例如:假设(player_id, event, points)has(1,A,10), (1,A,5), (1,A,2), (1,A,2), (1,A,1), (2,A,0)那么输出将是

player_id    event   points   rank
  1            A       10       1
  1            A        5       2
  1            A        2       3
  1            A        2       3
  1            A        1       5
  2            A        0       1

秩并不密集,所以如果你有重复的元组,你将有相同秩的输出元组以及你的秩数中的间隙。

要获取N每个的 top * 元组player_idevent您可以创建一个视图或在条件中使用子查询。视图是首选方式,但您无权在许多服务器上创建视图。

创建一个包含rankas 列的视图。

CREATE VIEW `points_view`
AS SELECT 
    `player_id`, `event`, `points`,
    (SELECT 1 + count(*) 
         FROM `points` 
         WHERE `l`.`player_id` = `player_id` 
             AND `l`.`event` = `event` 
             AND `points` > `l`.`points`
        ) as `rank`
FROM
    `points` `l`

从视图中获取所需的前 N ​​个结果:

SELECT
    `player_id`, `event`, `points`
FROM `points_view`
WHERE 
     `event` = 'A' AND `rank` <= 5
OR
     `event` = 'B' AND `rank` <= 2
OR
     `event` = 'C' AND `rank` <= 3

使用条件中的等级

SELECT 
    `player_id`, `event`, `points`
FROM
    `points` `l`
WHERE
    (SELECT 1 + count(*) 
     FROM `points` 
     WHERE `l`.`player_id` = `player_id` 
         AND `l`.`event` = `event` 
         AND `points` > `l`.`points`
    ) <= N

要根据您的事件进一步获得不同数量的元组,您可以这样做

SELECT 
    `player_id`, `event`, `points`
FROM
    `points` `l`
WHERE
        `event` = 'A' AND
        (SELECT 1 + count(*) 
         FROM `points` 
         WHERE `l`.`player_id` = `player_id` 
             AND `l`.`event` = `event` 
             AND `points` > `l`.`points`
        ) <= 5
    OR
        `event` = 'B' AND
        (SELECT 1 + count(*) 
         FROM `points` 
         WHERE `l`.`player_id` = `player_id` 
             AND `l`.`event` = `event` 
             AND `points` > `l`.`points`
        ) <= 2
    OR
        `event` = 'C' AND
        (SELECT 1 + count(*) 
         FROM `points` 
         WHERE `l`.`player_id` = `player_id` 
             AND `l`.`event` = `event` 
             AND `points` > `l`.`points`
        ) <= 3

我将只使用您的最大值N5 并忽略其他事件类型的其他元组,因为 MySQL 不会优化此查询,这会导致 3 个单独的依赖子查询。如果性能不是问题,或者您没有太多数据,请保持这种状态。

* 正如我所解释的那样,rank它并不密集,因此获取所有元组rank <= N通常会导致多于N元组。额外的元组是重复的。

从示例表中可以看出,简单地删除重复项是一个坏主意。如果您想要 and 的前 5 个结果player_id = 1,则event = A需要两个元组(1,A,2)。他们都有排名 3。但如果你删除其中一个,你最终只会得到前 4 个结果(1,A,10,1), (1,A,5,2), (1,A,2,3), (1,A,1,5)

要获得密集排名,您可以使用此子查询

(SELECT count(DISTINCT `points`) 
 FROM `points` 
 WHERE `l`.`player_id` = `player_id` 
     AND `l`.`event` = `event` 
     AND `points` >= `l`.`points`
) as `dense_rank`

小心,因为这仍然会产生重复的等级。

编辑

要将所有事件的分数加起来为一个分数,请使用GROUP BY

SELECT
    `player_id`, SUM(`points`)
FROM `points_view`
WHERE 
     `event` = 'A' AND `rank` <= 5
OR
     `event` = 'B' AND `rank` <= 2
OR
     `event` = 'C' AND `rank` <= 3

GROUP BY `player_id`
ORDER BY SUM(`points`) DESC

在分区 ( GROUP BY) 之前,结果包含正确数量的最高分,因此您可以简单地将所有分数相加。

您在这里面临的一个大问题是,既不rank也不dense_rank会给您工具为每个player_id和获得恰好 5 个元组event。例如:如果某人在事件 A 中获得 1000 倍 1 分,那么他最终将获得 1000 分,因为所有分数都会得到rankdense_rank 1

但又是这样ROWNUM:MySQL 不支持这个,所以我们必须模仿这个。问题ROWNUM在于它将为所有元组生成一个复合数字。但是我们想要 , 组的合player_idevent。不过,我仍在研究此解决方案。

编辑2

使用这个答案,我发现这个解决方案可以工作:

select
  player_id, sum( points )
from
(
select
  player_id,
  event,
  points,
  /* increment current_pos and reset to 0 if player_id or event changes */
  @current_pos := if (@current_player = player_id AND 
      @current_event = event, @current_pos, 0) + 1 as position,
  @current_player := player_id,
  @current_event := event
from
  (select 
    /* global variable init */
    @current_player := null, 
    @current_event := null, 
    @current_pos := 0) set_pos,
  points
order by
  player_id,
  event,
  points desc
) pos
WHERE
     pos.event = 'A' AND pos.position <= 5
OR
     pos.event = 'B' AND pos.position <= 2
OR
     pos.event = 'C' AND pos.position <= 3
GROUP BY player_id
ORDER BY SUM( points ) DESC

内部查询选择 (player_id, event, points)-元组,按 player_id 和 event 对它们进行排序,最后给每个元组一个复合数,每次 player_id 或 event 更改时重置为 0。由于顺序的原因,所有具有相同 player_id 的元组都是连续的。外部查询与先前使用的查询对视图的作用相同。

Edit3(见评论)

您可以使用 OLAP ROLLUP 运算符创建中间总和或不同类型的分区。例如,查询将如下所示:

select
  player_id, event, sum( points )
from
(
select
  player_id,
  event,
  points,
  /* increment current_pos and reset to 0 if player_id or event changes */
  @current_pos := if (@current_player = player_id AND 
      @current_event = event, @current_pos, 0) + 1 as position,
  @current_player := player_id,
  @current_event := event
from
  (select 
    /* global variable init */
    @current_player := null, 
    @current_event := null, 
    @current_pos := 0) set_pos,
  points
order by
  player_id,
  event,
  points desc
) pos
WHERE
     pos.event = 'A' AND pos.position <= 5
OR
     pos.event = 'B' AND pos.position <= 2
OR
     pos.event = 'C' AND pos.position <= 3
GROUP BY player_id, event WITH ROLLUP
/* NO ORDER BY HERE. SEE DOCUMENTATION ON MYSQL's ROLLUP FOR REASON */

结果现在将首先按 分组player_id, event,然后按 only分组,player_id最后按 null 分组(总结所有行)。

第一组看起来像(player_id, event, sum(points)) = {(1, A, 20), (1,B,5)}20 和 5 是关于player_id和的点的总和event。第二组的样子(player_id, event, sum(points)) = {(1,NULL,25)}。25 是关于 的所有点的总和player_id。希望有帮助。:-)

于 2012-04-13T01:50:42.593 回答
0

您可能需要为 sum(points) 命名。

这样做:

select player,sum(points) as points from table where event_type = "x" group by player order by points desc limit 5;

(我需要查看您的确切表架构才能将其写成您可以直接插入的内容,但这是它的要点)

于 2012-04-13T01:25:04.150 回答