
本文介绍如何将原本分两步执行的 sql 查询(先查球队列表,再为每个球队单独查最新赛事)合并为一条高效 sql,通过 left join 与 group by 配合聚合函数 max() 实现一次性获取所有球队及其最近一场已结束比赛(ft/pen/aet)的时间。
在实际开发中,嵌套循环查询(如 foreach 中反复执行子查询)极易引发“N+1 查询问题”,显著降低性能并增加数据库负载。针对您原始逻辑——先获取全部球队,再为每支队伍单独查询其最近一场状态为 'FT'、'PEN' 或 'AET' 的赛事——完全可通过单条 SQL 优化实现。
核心思路是:以 teams 表为主表,左连接 events 表,并利用条件关联(home_team = teams.id OR away_team = teams.id),再通过 GROUP BY teams.id 对每支队伍聚合,最后用 MAX(events.time) 提取该队所有匹配赛事中的最新时间。
以下是优化后的完整 SQL:
SELECT
teams.id,
teams.name,
teams.updated,
MAX(events.time) AS latest_time
FROM teams
LEFT JOIN events
ON events.home_team = teams.id
OR events.away_team = teams.id
WHERE
events.status IN ('FT', 'PEN', 'AET')
OR events.status IS NULL -- 保留无匹配赛事的球队(LEFT JOIN 的意义)
GROUP BY teams.id, teams.name, teams.updated
ORDER BY teams.name ASC;✅ 关键说明:
- LEFT JOIN 确保即使某支球队尚未参与任何已结束赛事(即 events 中无匹配记录),该球队仍会出现在结果中,此时 latest_time 为 NULL;
- WHERE 子句中显式包含 OR events.status IS NULL,防止 LEFT JOIN 后因 WHERE events.status IN (...) 将 NULL 行意外过滤(这是常见陷阱!);
- GROUP BY 必须包含 SELECT 中所有非聚合字段(即 teams.id, teams.name, teams.updated),符合 SQL 标准(尤其在 MySQL 5.7+ 严格模式下);
- 若仅需有赛事记录的球队,可改用 INNER JOIN 并简化 WHERE 条件(去掉 IS NULL 分支)。
? 进阶建议:若需获取整条最新赛事记录(不止 time 字段),则需改用窗口函数(MySQL 8.0+)或相关子查询,但当前场景中 MAX() 已精准满足需求,简洁且高效。
综上,该单查询不仅消除循环开销,还提升可维护性与可读性,是典型的关系型数据库“以集合思维替代过程思维”的最佳实践。










