球队积分:主客场两种身份都要算
LeetCode 1212 · 条件求和、OR 关联、零值保留
面试怎么回答
我会先把每场比赛展开成主队一行、客队一行,各自按胜平负算出 3/1/0 分,UNION ALL 后用球队表 LEFT JOIN 汇总,IFNULL 补 0,按积分降序、ID 升序排序。展开后每行只表达一支球队的一场比赛,比在一条 SUM 里写四个 IF 更不容易错。
先把这段讲顺;面试官追问时,再展开下面的原理和细节。
详细讲解
题意
球队表和比赛表(主队、客队、双方进球)。赢一场 3 分、平 1 分、输 0 分。算出每支球队的总积分,按积分降序、积分相同按球队 ID 升序;没参赛的球队积分为 0。
表结构
- Teams:team_id(主键)、team_name
- Matches:match_id(主键)、host_team、guest_team、host_goals、guest_goals
思路
一支球队可能是主队也可能是客队,用球队表 LEFT JOIN 比赛表、条件写成「是主队或是客队」,就拿到了它参加的所有比赛。再用 CASE 按它在这场是主还是客、比分如何算出得分,按球队汇总。LEFT JOIN 保证没比赛的球队也在,积分用 IFNULL 补 0。
参考解法
select team_id , team_name ,sum(
if(team_id = host_team && host_goals>guest_goals ,3,0)+
if(team_id = host_team && host_goals=guest_goals,1,0)+
if(team_id = guest_team && host_goals=guest_goals,1,0)+
if(team_id = guest_team && host_goals<guest_goals ,3,0)
)num_points
from Teams t left join Matches m
on t.team_id =m.host_team or t.team_id =m.guest_team
group by team_id ,team_name
order by num_points desc,team_id
另一种写法:先把每场比赛展开成两支球队各一行,再汇总
SELECT t.team_id, t.team_name, IFNULL(SUM(s.pts), 0) AS num_points
FROM Teams t
LEFT JOIN (
SELECT host_team AS team, CASE WHEN host_goals > guest_goals THEN 3 WHEN host_goals = guest_goals THEN 1 ELSE 0 END AS pts FROM Matches
UNION ALL
SELECT guest_team, CASE WHEN guest_goals > host_goals THEN 3 WHEN host_goals = guest_goals THEN 1 ELSE 0 END FROM Matches
) s ON t.team_id = s.team
GROUP BY t.team_id, t.team_name
ORDER BY num_points DESC, t.team_id
易错点
- 只关联主队或只关联客队,会漏掉一半比赛。
- 没参赛的球队 SUM 结果是 NULL,要补 0。
- 也可以先把比赛展开成「每队每场一行」再汇总,逻辑更清楚。