题目描述

✅ 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) 转成零。已经参加比赛但全部失利的球队,求和本身也自然为零。

最后按总积分降序、球队编号升序排序,满足排名和同分规则。聚合基于全部主客贡献,球队列表又来自完整主表,因此既不会漏计比赛,也不会漏掉无比赛球队。

解题步骤

  1. 从比赛表生成主队编号和主队积分记录。
  2. 对称生成客队记录,用 UNION ALL 保留并合并所有贡献。
  3. 从 Teams 左连接这些明细,按球队编号和名称分组计算积分和。
  4. 将无贡献的空聚合值补为零,再按积分降序、球队编号升序返回。

代码实现

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。
转载与许可
作者
链接 https://hgnulb.github.io/blog/2022/59322178
许可 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!