[英]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_id
和score
为所有球员和他们的分数。
然后汇总得到每个玩家的总分。
最后将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.