如何仅对该查询中的 3 个最佳分数求和?

How do I sum only the 3 best scores from this query?

我只需要对 11 场比赛中每位选手的 5 个最佳成绩求和。下面的查询创建了一个排行榜,它总结了所有的分数,但我对如何只能总结 5 个最好的分数感到困惑。

我已将查询缩写为仅显示 4 个事件并获得最佳 3 个结果以缩短 post,但希望它能理解我所需要的。

SELECT playerID AS Player,
  SUM(CASE WHEN championshipleaderboard.eventID = 1 THEN championshipleaderboard.points ELSE 0 END) AS Event1,
  SUM(CASE WHEN championshipleaderboard.eventID = 2 THEN championshipleaderboard.points ELSE 0 END) AS Event2,
  SUM(CASE WHEN championshipleaderboard.eventID = 3 THEN championshipleaderboard.points ELSE 0 END) AS Event3,
  SUM(CASE WHEN championshipleaderboard.eventID = 4 THEN championshipleaderboard.points ELSE 0 END) AS Event4,
  SUM(championshipleaderboard.points) AS Total 
FROM (
  championshipleaderboard JOIN members ON championshipleaderboard.playerId = members.playerId
)
GROUP BY championshipleaderboard.playerId
ORDER BY Total DESC;

table:冠军排行榜

+--------+---------+--------+
| Player | EventID | Points |
+--------+---------+--------+
|      1 |       1 |     25 |
|      2 |       1 |     20 |
|      1 |       2 |     15 |
|      2 |       2 |     13 |
|      1 |       3 |     20 |
|      2 |       3 |     12 |
|      1 |       4 |     20 |
|      2 |       4 |     10 |
+--------+---------+--------+

当前结果是

+--------+--------+--------+--------+--------+-------+
| Player | Event1 | Event2 | Event3 | Event4 | Total |
+--------+--------+--------+--------+--------+-------+
|      1 |     25 |     15 |     20 |     20 |    80 |
|      2 |     20 |     13 |     12 |     10 |    55 |
+--------+--------+--------+--------+--------+-------+

需要的结果是

+--------+--------+--------+--------+--------+--------------+
| Player | Event1 | Event2 | Event3 | Event4 | Total(best3) |
+--------+--------+--------+--------+--------+--------------+
|      1 |     25 |     15 |     20 |     20 |           65 |
|      2 |     20 |     13 |     12 |     10 |           45 |
+--------+--------+--------+--------+--------+--------------+

您可以使用相关的子查询来找到第三个最好的分数,并且分数总和等于 to/greater 第三个最好的分数:

SELECT Player
     , MIN(CASE WHEN Eventid = 1 THEN points END) AS Event1
     , MIN(CASE WHEN Eventid = 2 THEN points END) AS Event2
     , MIN(CASE WHEN Eventid = 3 THEN points END) AS Event3
     , MIN(CASE WHEN Eventid = 4 THEN points END) AS Event4
     , SUM(CASE WHEN (Points, ID) >= (
        SELECT Points, ID
        FROM t AS x
        WHERE x.Player = t.Player
        ORDER BY Points DESC, ID DESC
        LIMIT 2, 1
     ) THEN Points END) AS TopThree
FROM t
GROUP BY Player

请注意,已排序点之间的关系因 Points DESC, ID DESC 的排序而中断;因此,如果玩家的得分为 (1, 20), (2, 20), (3, 30), (4, 30),则将 (4, 30), (3, 30), (2, 20) 相加。

Demo on DB<>Fiddle

我建议在按 concat(player ID, event ID) 分组的查询中使用子查询,限制为 3(或 5,或更多)。

在我自己的数据库中的类似表上对此进行了测试并且有效。

已编辑 SQL:

SELECT cl.eventID, m.playerID,
   (SELECT 
        SUM(cl2.points)
    FROM
        championshipleaderboard cl2
    WHERE
        cl2.playerID = pr.playerID
            AND cl2.eventID = cl.eventID
    ORDER BY cl2.points DESC
    LIMIT 3) AS top_x_sum
 FROM championshipleaderboard cl, members m
   WHERE cl.playerID = m.playerID

 GROUP BY CONCAT(cl.eventID, m.playerID);

使用rank

现场测试:https://www.db-fiddle.com/f/4ufuFAXKf7mi5yefNQqoXM/2

with ranking as
(
   select 
      player,       
      rank() over(partition by player order by points desc) as xrank,
      points
  from tbl
)
,pick3 as
(
  select player, sum(points) as best3
  from ranking 
  where xrank <= 3
  group by player
)
select
   t.player, 
   sum(if(t.eventid = 1, t.points,0)) as event1,
   sum(if(t.eventid = 2, t.points,0)) as event2,
   sum(if(t.eventid = 3, t.points,0)) as event3,  
   sum(if(t.eventid = 4, t.points,0)) as event4,
   p.best3            
from tbl t
join pick3 p on t.player = p.player
group by t.player

输出:

| player | event1 | event2 | event3 | event4 | best3 |
| ------ | ------ | ------ | ------ | ------ | ----- |
| 1      | 25     | 15     | 20     | 20     | 65    |
| 2      | 20     | 13     | 12     | 10     | 45    |

另一种方法:https://www.db-fiddle.com/f/332F6XA3J3GaxXeD7LFP81/0

现场测试:

with ranking as
(
    select
        player,       
        rank() over(partition by player order by points desc) as xrank,
        points
    from tbl
)
select
    player, 
    sum(if(eventid = 1, points, 0)) as event1,
    sum(if(eventid = 2, points, 0)) as event2,
    sum(if(eventid = 3, points, 0)) as event3,  
    sum(if(eventid = 4, points, 0)) as event4,
    sum(
        if(
            (player,points) in
                (select player,points 
                from ranking 
                where xrank <= 3), 
            points, 
            0 
        ) 
    ) as best3
from tbl
group by player

输出:

| player | event1 | event2 | event3 | event4 | best3 |
| ------ | ------ | ------ | ------ | ------ | ----- |
| 1      | 25     | 15     | 20     | 20     | 65    |
| 2      | 20     | 13     | 12     | 10     | 45    |

MySQL 5.7

现场测试:https://www.db-fiddle.com/f/4ufuFAXKf7mi5yefNQqoXM/15

select
    t.player, 
    sum(case when t.eventid = 1 then t.points end) as event1,
    sum(case when t.eventid = 2 then t.points end) as event2,
    sum(case when t.eventid = 3 then t.points end) as event3,
    sum(case when t.eventid = 4 then t.points end) as event4,

    sum(
        case when t.points >= (
            select best3.points 
            from tbl best3 
            where best3.player = t.player
            order by best3.points desc 
            limit 1 offset 2 -- offset starts with 0. so 2 is the third
        ) then 
            t.points
        end
    ) as best3            
from tbl t
group by t.player;

输出:

| player | event1 | event2 | event3 | event4 | best3 |
| ------ | ------ | ------ | ------ | ------ | ----- |
| 1      | 25     | 15     | 20     | 20     | 65    |
| 2      | 20     | 13     | 12     | 10     | 45    |

这是一种方法 - 尽管在 MySQL 8+ 中,您会使用更多现代技术...

DROP TABLE IF EXISTS my_table;

CREATE TABLE my_table
(Player INT NOT NULL
,EventID INT NOT NULL
,Points INT NOT NULL
,PRIMARY KEY(player,eventid)
);

INSERT INTO my_table VALUES
(1,1,25),
(2,1,20),
(1,2,15),
(2,2,13),
(1,3,20),
(2,3,12),
(1,4,20),
(2,4,10);

SELECT player
     , SUM(points) top3 
  FROM 
     ( SELECT x.*
            , CASE WHEN @prev = player THEN @i:=@i+1 ELSE @i:=1 END i
            , @prev:=player 
         FROM my_table x
            , (SELECT @prev:=null,@i:=0) vars ORDER BY player,points
DESC) a 
        WHERE i<=3 
        GROUP 
           BY player;
+--------+------+
| player | top3 |
+--------+------+
|      1 |   65 |
|      2 |   45 |
+--------+------+