LeetCode 1212. 查询球队积分
题目描述
题意分析
要的是一张「每支球队的总积分」表,输出三列:
team_id、team_name、num_points,按积分降序、球队编号升序排列。
计分规则由两张表共同决定:
Matches的一行记录里,主队和客队各自要根据比分拿分,胜 3 分、平 1 分、负 0 分。也就是说同一行比赛要对两支不同球队产生贡献,一支球队又可能出现在host_team或guest_team任意一侧。
约束里最关键的一句是「
Teams里的球队可能一场比赛都没打」。这句话直接决定了必须保留没有比赛记录的球队,并且把它的积分补成 0 而不是 NULL,这是本题唯一真正的坑。
边界:球队零场比赛、积分并列、比分为 0 比 0 的平局、
Matches整表为空。
解法:SQL 查询建模
核心思路
一场比赛横向存着主队和客队,但最终要按球队聚合。先用
UNION ALL把每场比赛规范化成两行(team_id, points):主队一行、客队一行,各自用CASE根据比分计算 3、1、0 分。后续只面对统一的「某队在某场比赛得到多少分」记录,可以用等值连接和普通求和完成统计。
规范化后的不变量是:每场比赛恰好贡献两行,而且每行积分恰好等于对应球队在该场比赛的得分。因此按
team_id求和,会完整计入该队所有主场和客场积分,也不会重复或漏算。这里必须用UNION ALL;若用UNION,同一球队多场比赛得到相同分数的记录会被去重。
最外层以
Teams为主表做LEFT JOIN,保证零场球队仍有一行;这类球队的SUM(p.points)为NULL,用COALESCE(..., 0)转成题目要求的 0。最后按完整规则排序:积分降序,积分相同时球队编号升序。
解题步骤
- 第一个子查询输出主队积分:主队进球更多得 3 分,相等得 1 分,否则得 0 分。
- 第二个子查询对客队做对称计算,用
UNION ALL保留所有比赛贡献。中间表每行都统一为(team_id, points)。- 从
Teams左连接积分明细,按team_id, team_name分组并求和;没有明细的球队用COALESCE补 0。- 按
num_points DESC, team_id ASC排序。第二个键处理积分并列,不能省略。
以
Teams = [(1,'A'),(2,'B'),(3,'C'),(4,'D'),(5,'E')]、Matches = [(1,1,2,0,1),(2,2,3,2,2),(3,3,4,1,3)]为例,规范化后得到 A:0,B:3、1,C:1、0,D:3 六条积分记录。分组求和得到 A=0、B=4、C=1、D=3;E 没有匹配记录,由COALESCE得 0。最终顺序是 B(4)、D(3)、C(1)、A(0)、E(0),A 与 E 同分时按较小team_id在前。
代码实现
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 \log T)$,其中 $M$ 是比赛数、$T$ 是球队数。两次扫描比赛表产生 $2M$ 条明细,聚合与等值连接处理线性规模的数据,最终对 $T$ 支球队排序。实际代价由索引和执行计划决定。
- 空间复杂度:最坏 $O(M + T)$;执行器可能物化 $2M$ 条规范化明细,并维护球队聚合与排序状态。
关键点总结
- 一行比赛同时描述两个实体的贡献时,先用
UNION ALL规范化成长表,再聚合;统一数据形状后,主客场差异不会泄漏到外层逻辑。UNION ALL是正确性要求,不只是性能选择:UNION会把相同的(team_id, points)比赛贡献去重。- 输出要求覆盖所有球队,所以必须以
Teams为主表左连接;COALESCE再把「无明细」转换为 0 分。CASE的三个分支分别对应胜、平、负,主队和客队的比较方向要对称。- 排序键必须完整写出积分降序和球队编号升序,保证并列结果确定。
易错点总结
- 错误写法:用
UNION代替UNION ALL。若同一球队两场都获胜,两条(team_id, 3)会被合并成一条,总分从 6 错成 3。- 错误写法:客队分支仍写
host_goals > guest_goals THEN 3。比赛 A 0:1 B 时,B 本应得 3 分,却会被算成 0。- 错误写法:从积分明细出发或使用
INNER JOIN。示例中的 E 没有比赛记录,会从结果中消失,而题目要求输出 E 的 0 分。- 错误写法:省略
COALESCE。零场球队虽然被LEFT JOIN保留,但SUM(NULL)的结果仍是NULL,不是 0。- 错误写法:只按积分降序。A 与 E 都是 0 分时次序不确定,必须追加
team_id ASC。
相似题目
| 题目 | 难度 | 考察点 |
|---|---|---|
| 175. 组合两个表 | 简单 | 最小化的 LEFT JOIN,只保行不做聚合 |
| 1280. 学生们参加各科测试的次数 | 简单 | 需要先 CROSS JOIN 造出全量组合再左连计数 |
| 1107. 每日新用户统计 | 中等 | 先用子查询取每人首次行为日期,再按日期聚合 |
| 1126. 查询活跃业务 | 中等 | 聚合结果反过来作为过滤条件,需要两轮聚合 |