多表查询优化:院校年度招生统计及总人数计算咨询
嘿,这个问题我太有发言权了!你现在的循环创建PreparedStatement的做法不仅代码冗余,还会带来额外的数据库连接开销和对象GC压力,完全可以用SQL关联+分组聚合的方式一步搞定,效率和代码简洁度都能提升一大截。
最优解决方案:用SQL一次性完成统计
首先咱们先明确两张表的结构(我先按常规场景假设,你可以根据实际表名/字段名调整):
- 院校表:
colleges,包含college_id(主键)、college_name(院校名称) - 招生数据表:
enrollment_data,包含college_id(关联院校ID)、year(年份)、enrollment_count(招生人数)
1. 统计各院校每年的招生人数
直接通过表关联+分组聚合,就能一次性拿到所有院校的年度招生数据:
SELECT c.college_name, ed.year, SUM(ed.enrollment_count) AS annual_enrollment FROM colleges c JOIN enrollment_data ed ON c.college_id = ed.college_id GROUP BY c.college_name, ed.year ORDER BY ed.year, c.college_name;
如果你的enrollment_data里每条记录就是对应院校当年的总招生数,那SUM可以换成MAX或者直接取ed.enrollment_count,但SUM更通用(比如同一年同一院校有多条细分招生记录的场景)。
2. 同时统计每年的总招生人数(带汇总行)
如果需要在结果里同时输出每年所有院校的总招生人数,可以用ROLLUP来生成分组汇总行,不用额外写查询:
SELECT CASE WHEN GROUPING(c.college_name) = 1 THEN '年度总招生' ELSE c.college_name END AS college_name, ed.year, SUM(ed.enrollment_count) AS annual_enrollment FROM colleges c JOIN enrollment_data ed ON c.college_id = ed.college_id GROUP BY ed.year, c.college_name WITH ROLLUP HAVING ed.year IS NOT NULL -- 过滤掉全表的总汇总行,只保留每年的汇总 ORDER BY ed.year, GROUPING(c.college_name), c.college_name;
这里的GROUPING函数用来判断当前行是不是汇总行:返回1表示是汇总行(也就是年度总招生),0表示是普通院校的行。
Java代码实现(仅创建一次PreparedStatement)
现在Java端只需要执行一次查询,处理结果集即可,完全不需要循环遍历院校ID:
// 替换成你的实际SQL(上面两种二选一) String sql = "SELECT " + " CASE " + " WHEN GROUPING(c.college_name) = 1 THEN '年度总招生' " + " ELSE c.college_name " + " END AS college_name, " + " ed.year, " + " SUM(ed.enrollment_count) AS annual_enrollment " + "FROM colleges c " + "JOIN enrollment_data ed ON c.college_id = ed.college_id " + "GROUP BY ed.year, c.college_name WITH ROLLUP " + "HAVING ed.year IS NOT NULL " + "ORDER BY ed.year, GROUPING(c.college_name), c.college_name"; // 用try-with-resources自动关闭资源,避免内存泄漏 try (Connection conn = getYourDatabaseConnection(); // 替换成你的获取连接方法 PreparedStatement pstmt = conn.prepareStatement(sql); ResultSet rs = pstmt.executeQuery()) { // 遍历结果集输出或处理数据 while (rs.next()) { String collegeName = rs.getString("college_name"); int year = rs.getInt("year"); int enrollment = rs.getInt("annual_enrollment"); System.out.printf("%s | %d年 | 招生人数:%d%n", collegeName, year, enrollment); } } catch (SQLException e) { // 这里可以根据业务做异常处理,比如日志记录 e.printStackTrace(); }
为什么这比循环方案更好?
- 减少数据库开销:一次查询代替多次循环查询,降低了网络IO和数据库连接的压力
- 避免冗余对象创建:不需要循环创建
PreparedStatement,减少了对象实例化和GC的负担 - 利用数据库优化:数据库在分组聚合查询上的优化(比如索引、执行计划)比Java循环高效得多
- 代码更简洁:逻辑集中在SQL里,Java代码只负责执行和处理结果,维护性更强
额外优化建议
- 给
enrollment_data表的college_id和year字段建立联合索引,能大幅提升JOIN和GROUP BY的执行效率 - 如果需要包含当年没有招生数据的院校(比如某院校某年停招),可以把
JOIN换成LEFT JOIN,并用COALESCE把NULL招生数转为0:SELECT c.college_name, ed.year, COALESCE(SUM(ed.enrollment_count), 0) AS annual_enrollment FROM colleges c LEFT JOIN enrollment_data ed ON c.college_id = ed.college_id GROUP BY c.college_name, ed.year ORDER BY ed.year, c.college_name;
内容的提问来源于stack exchange,提问作者vidhu
相关产品推荐
相关产品推荐

