You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

多表查询优化:院校年度招生统计及总人数计算咨询

嘿,这个问题我太有发言权了!你现在的循环创建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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 10:23:55