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);
注解方式需要注意:
- 使用
