MyBatis中GROUP BY与HAVING条件组合应用解析

1. MyBatis中GROUP BY与HAVING条件组合应用解析

在数据库查询中,GROUP BY与HAVING的组合使用是进行分组后筛选的利器。不同于WHERE子句在分组前过滤数据,HAVING专门用于对分组后的结果集进行条件筛选。MyBatis作为Java生态中广泛使用的ORM框架,对这类复杂SQL查询提供了完善的支持。

实际开发中经常遇到这样的场景:需要先按某些字段分组,然后对分组后的聚合结果进行多条件筛选。比如统计各部门薪资水平时,我们可能只想查看平均工资超过1万元且最高工资低于5万元的部门。这种需求就需要在GROUP BY后使用多个HAVING条件组合。

需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。

2. HAVING多条件组合的实现方式

2.1 基础XML配置写法

最直接的实现方式是在MyBatis的Mapper XML文件中编写完整SQL:

xml复制<select id="selectGroupWithHaving" resultType="map">
    SELECT department_id, AVG(salary) avg_salary, MAX(salary) max_salary
    FROM employees
    GROUP BY department_id
    HAVING AVG(salary) > 10000 
    AND MAX(salary) < 50000
</select>

这种写法的优点是直观明了,适合条件固定且数量较少的场景。但当条件动态变化时,就需要使用MyBatis的动态SQL特性。

2.2 动态SQL实现方式

MyBatis提供了强大的动态SQL功能,可以灵活构建HAVING条件:

xml复制<select id="selectDynamicHaving" resultType="map">
    SELECT department_id, AVG(salary) avg_salary, COUNT(*) emp_count
    FROM employees
    GROUP BY department_id
    <if test="minAvgSalary != null">
        HAVING AVG(salary) > #{minAvgSalary}
    </if>
    <if test="maxEmpCount != null">
        AND COUNT(*) < #{maxEmpCount}
    </if>
</select>

这里需要注意第一个条件前必须有HAVING关键字,后续条件使用AND连接。这种写法在条件可能为空时会产生语法错误,需要进一步优化。

3. 使用trim标签优化多条件

3.1 trim标签的基本用法

MyBatis的trim标签可以智能处理前缀后缀,完美解决HAVING条件动态拼接问题:

xml复制<select id="selectWithTrim" resultType="map">
    SELECT department_id, AVG(salary) avg_salary, MAX(salary) max_salary
    FROM employees
    GROUP BY department_id
    <trim prefix="HAVING" prefixOverrides="AND|OR">
        <if test="minAvgSalary != null">
            AND AVG(salary) > #{minAvgSalary}
        </if>
        <if test="maxSalary != null">
            AND MAX(salary) < #{maxSalary}
        </if>
        <if test="minEmpCount != null">
            AND COUNT(*) > #{minEmpCount}
        </if>
    </trim>
</select>

trim标签的运作机制:

  • prefix:当trim内有内容时,添加指定的前缀(这里是"HAVING")
  • prefixOverrides:自动移除内容开头多余的AND或OR
  • suffixOverrides:自动移除内容结尾多余的AND或OR(本例未使用)

3.2 复杂条件组合示例

对于更复杂的条件逻辑,可以结合choose/when/otherwise:

xml复制<trim prefix="HAVING" prefixOverrides="AND|OR">
    <choose>
        <when test="strictMode">
            AND AVG(salary) > 15000
            AND MAX(salary) < 40000
        </when>
        <otherwise>
            <if test="minAvgSalary != null">
                AND AVG(salary) > #{minAvgSalary}
            </if>
        </otherwise>
    </choose>
    <if test="statusList != null and statusList.size() > 0">
        AND status IN 
        <foreach item="status" collection="statusList" open="(" separator="," close=")">
            #{status}
        </foreach>
    </if>
</trim>

4. 注解方式实现方案

除了XML配置,MyBatis也支持注解方式实现复杂查询:

java复制@Select("<script>" +
        "SELECT role, COUNT(*) as count FROM users " +
        "GROUP BY role " +
        "<trim prefix='HAVING' prefixOverrides='AND|OR'>" +
        "   <if test='minCount != null'> AND COUNT(*) > #{minCount} </if>" +
        "   <if test='maxCount != null'> AND COUNT(*) < #{maxCount} </if>" +
        "</trim>" +
        "</script>")
List<Map<String, Object>> groupByRoleWithHaving(@Param("minCount") Integer minCount, 
                                              @Param("maxCount") Integer maxCount);

注解方式需要注意:

  1. 使用