繁体   English   中英

SQL查询,如何改进?

[英]SQL query, how to improve?

我做了一个编写 SQL 查询的任务,我想知道我是否可以以某种方式改进它。

描述:

假设我们在某个在线服务上有一个数据库

让我们创建表,并插入一些数据

create table players (
    player_id integer not null unique,
    group_id integer not null
);

create table matches (
    match_id integer not null unique,
    first_player integer not null,
    second_player integer not null,
    first_score integer not null,
    second_score integer not null
);

insert into players values(20, 2);
insert into players values(30, 1);
insert into players values(40, 3);
insert into players values(45, 1);
insert into players values(50, 2);
insert into players values(65, 1);
insert into matches values(1, 30, 45, 10, 12);
insert into matches values(2, 20, 50, 5, 5);
insert into matches values(13, 65, 45, 10, 10);
insert into matches values(5, 30, 65, 3, 15);
insert into matches values(42, 45, 65, 8, 4);

查询的输出应该是:

group_id | winner_id
--------------------
1        | 45
2        | 20
3        | 40

所以,我们应该输出每组的获胜者(玩家 ID)。 获胜者是在比赛中获得最大积分的玩家。

如果用户独自在组中 - 他自动成为获胜者,如果玩家拥有相同的积分 - 获胜者是具有较低 id 值的人。

输出应按 group_id 字段排序

我的解决方案:

SELECT 
  results.group_id, 
  results.winner_id 
FROM 
  (
    SELECT 
      summed.group_id, 
      summed.player_id AS winner_id, 
      MAX(summed.sum) AS total_score 
    FROM 
      (
        SELECT 
          mapped.player_id, 
          mapped.group_id, 
          SUM(mapped.points) AS sum 
        FROM 
          (
            SELECT 
              p.player_id, 
              p.group_id, 
              CASE WHEN p.player_id = m.first_player THEN m.first_score WHEN p.player_id = m.second_player THEN m.second_score ELSE 0 END AS points 
            FROM 
              players AS p 
              LEFT JOIN matches AS m ON p.player_id = m.first_player 
              OR p.player_id = m.second_player
          ) AS mapped 
        GROUP BY 
          mapped.player_id
      ) as summed 
    GROUP BY 
      summed.group_id 
    ORDER BY 
      summed.group_id
  ) AS results;

它有效,但我 99% 确定它可以更清洁。 将不胜感激任何建议

首先,使用UNION ALL来提取matches 2列: player_idscore为所有球员和他们的分数。
然后汇总得到每个玩家的总分。
最后将players LEFT连接到您获得的结果集,使用GROUP_CONCAT()按总分降序收集每个组的所有玩家,并使用SUBSTRING_INDEX()选择第一个玩家:

SELECT p.group_id, 
       SUBSTRING_INDEX(GROUP_CONCAT(p.player_id ORDER BY t.score DESC, p.player_id), ',', 1) winner_id
FROM players p
LEFT JOIN (
  SELECT player_id, SUM(score) score
  FROM (
    SELECT first_player player_id, first_score score FROM matches
    UNION ALL
    SELECT second_player, second_score FROM matches
  ) t  
  GROUP BY player_id
) t ON t.player_id = p.player_id  
GROUP BY p.group_id;

请参阅演示

请注意,通过执行LEFT连接,您将获得所有组的结果,即使是那些没有任何玩家参加任何比赛的组(就像您的样本数据一样),在这种情况下,获胜者是任意玩家(只是就像您的预期结果一样)。

您可以取消对比赛表的旋转并对每个玩家的积分求和(我认为这是您想要的):

select p.player_id, p.group_id, sum(score) as sum_score
from ((select first_player as player_id, first_score as score
       from matches
      ) union all
      (select second_player as player_id, second_score as score
       from matches
      )
     ) mp
     players p
     using (player_id)
group by p.player_id, p.group_id;

接下来可以引入一个窗口函数来获取top:

select player_id, group_id, sum_score
from (select p.player_id, p.group_id, sum(score) as sum_score,
             row_number() over (partition by p.group_id order by sum(score) desc p.player_id asc) as seqnum
      from ((select first_player as player_id, first_score as score
             from matches
            ) union all
            (select second_player as player_id, second_score as score
             from matches
            )
           ) mp
           players p
           using (player_id)
      group by p.player_id, p.group_id
     ) pg
where seqnum = 1;

如果您确实想要所有匹配项的最高分数而不是sum() ,则使用max()而不是sum()

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM