LeetCode 1212. 查询球队积分
题目描述
题意分析
Teams保存球队编号和名称,Matches保存每场比赛的主客队以及双方进球数。赢一场得三分,平局双方各得一分,输的一方得零分,要求统计每支球队的总积分。输出必须包含全部球队,没有参加比赛的球队也要显示零分。结果先按总积分从高到低排列,同分时按球队编号从小到大排列;每一场比赛都独立贡献积分,不能因为得分相同就合并掉比赛。
解法:SQL 查询建模
核心思路
[!blue]
一行比赛同时涉及两支球队,直接按主队分组会漏掉它作为客队获得的积分。先把每场比赛拆成两条统一格式的贡献记录:一条是主队编号及主队得分,另一条是客队编号及客队得分。
主队的
CASE比较host_goals与guest_goals,大于得三分,相等得一分,否则为零。客队反过来比较自己的进球数,才能从客队角度得到相应积分。两部分都命名为team_id和points,后续就不必再区分主客身份。使用
UNION ALL合并贡献明细,让每场的每支球队都保留一条记录。不同比赛可能产生完全相同的(team_id, points),它们仍然都应累加;普通UNION的去重会错误丢掉这些有效贡献。以完整的
Teams为左表连接积分明细,再按球队编号和名称分组求和。即使没有对应比赛记录,左连接仍会留下该球队;此时SUM(p.points)为NULL,用COALESCE(..., 0)转成零。已经参加比赛但全部失利的球队,求和本身也自然为零。最后按总积分降序、球队编号升序排序,满足排名和同分规则。聚合基于全部主客贡献,球队列表又来自完整主表,因此既不会漏计比赛,也不会漏掉无比赛球队。
解题步骤
- 从比赛表生成主队编号和主队积分记录。
- 对称生成客队记录,用
UNION ALL保留并合并所有贡献。- 从
Teams左连接这些明细,按球队编号和名称分组计算积分和。- 将无贡献的空聚合值补为零,再按积分降序、球队编号升序返回。
代码实现
SELECT
t.team_id,
t.team_name,
-- 未参加比赛的球队也要返回零分。
COALESCE(SUM(p.points), 0) AS num_points
FROM Teams AS t
-- 以球队表为主,保留没有比赛明细的球队。
LEFT JOIN (
SELECT
host_team AS team_id,
CASE
WHEN host_goals > guest_goals THEN 3
WHEN host_goals = guest_goals THEN 1
ELSE 0
END AS points
FROM Matches
-- 每场贡献分别保留,相同球队相同得分不能去重。
UNION ALL
SELECT
guest_team AS team_id,
CASE
WHEN guest_goals > host_goals THEN 3
WHEN guest_goals = host_goals THEN 1
ELSE 0
END AS points
FROM Matches
) AS p ON p.team_id = t.team_id
GROUP BY t.team_id, t.team_name
ORDER BY num_points DESC, t.team_id ASC;
复杂度分析
- 时间复杂度:在使用哈希聚合和等值连接的常见计划下,处理比赛和球队约为 $O(M+T)$,最终球队排序为 $O(T\log(T+1))$,合计 $O(M+T\log(T+1))$;
M为比赛数,T为球队数,实际依赖执行计划。- 空间复杂度:若物化两份比赛贡献并保存分组和排序结果,最坏为 $O(M+T)$;是否物化及采用何种连接由数据库决定。
关键点总结
[!green]
- 每场拆成主客两条贡献,把不同角色统一成球队维度后再聚合。
UNION ALL保留每场发生的得分,不能用按内容去重替代。- 球队主表左连接保证完整名单,
COALESCE将没有比赛的空结果转换为零分。
易错点总结
[!yellow]
- 仅统计主队或仅统计客队,会漏掉球队在另一种身份下获得的积分。
- 客队分支沿用主队比较方向,会把胜负积分颠倒。
- 使用
UNION,会删除同一球队在不同比赛获得的相同分数记录。- 改用内连接,或在外层过滤掉右表为空的记录,会让未参赛球队消失。
- 只按积分排序,无法保证同分球队按编号升序输出。
相似题目
| 题目 | 难度 | 关联与区别 |
|---|---|---|
| 175. 组合两个表 | 简单 | 同样从必须完整保留的主表做左连接,本题无比赛球队仍要显示且积分补0。 |
转载与许可
许可
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!