如何合并两个HQL统计查询?单次调用数据库获取两类结果
合并两类统计查询为单次数据库调用的方案
问题描述
我有两类统计查询需求:
- 按
type_id分组统计所有记录(无论is_read状态)的distinct ta.id数量,后续在Java代码中汇总各组总数; - 统计所有
is_read='N'的记录的distinct ta.id总数。
目前需要两次调用数据库分别执行这两个查询,希望合并为单次数据库调用同时获取两类结果,实际使用HQL编写查询,以下是便于理解的SQL版本:
原查询1:按type_id分组统计全状态记录
-- 统计所有状态下的记录,后续在Java中汇总各组总数 select type_id as type, count(distinct ta.id) as totalId from tableA as ta inner join tableB tb on ta.id = tb.id inner join tableC tc on tb.col1= tc.col2 where tc.col2= 6 and tb.is_deleted = 'N' group by type_id
原查询2:统计未读记录总数
-- 仅统计未读(is_read='N')的记录总数 select count(distinct ta.id) as totalId from tableA as ta inner join tableB tb on ta.id =tb.id inner join tableC tc on tb.col1 = tc.col2 where tc.col2= 6 and tb.is_deleted = 'N' and tb.is_read = 'N'
解决方案
完全可以通过条件聚合或者union all的方式把两个查询合并成一次调用,同时拿到你需要的两类结果,而且写法兼容HQL,下面给你两种可行方案:
方案一:条件聚合+子查询(推荐)
这种方式会在每条分组记录里同时返回该分组的全状态统计数,以及全局的未读总数,结果结构更规整:
select type_id as type, count(distinct ta.id) as totalAllStatus, -- 对应原查询1的分组统计结果 max(global_unread.total) as globalUnreadTotal -- 对应原查询2的全局未读总数 from tableA ta inner join tableB tb on ta.id = tb.id inner join tableC tc on tb.col1 = tc.col2 -- 子查询单独计算全局未读总数,用cross join把这个值带到每一行 cross join ( select count(distinct ta_inner.id) as total from tableA ta_inner inner join tableB tb_inner on ta_inner.id = tb_inner.id inner join tableC tc_inner on tb_inner.col1 = tc_inner.col2 where tc_inner.col2 = 6 and tb_inner.is_deleted = 'N' and tb_inner.is_read = 'N' ) global_unread where tc.col2 = 6 and tb.is_deleted = 'N' group by type_id, global_unread.total
totalAllStatus:每个type_id下所有状态的去重ta.id数量,和原查询1结果完全一致;globalUnreadTotal:所有未读记录的去重ta.id总数,每条分组返回的该值都相同,在Java中取任意一条的该值即可;- 若需要每个分组自身的未读数量,可额外添加
count(distinct case when tb.is_read = 'N' then ta.id end) as groupUnread,按需调整。
方案二:用union all合并结果集
这种方式把两个查询的结果拼在一起,用标识字段区分数据类型,适合需要明确分开两类结果的场景:
-- 第一部分:原查询1的分组统计 select type_id as type, count(distinct ta.id) as count, 'all_group' as statType from tableA ta inner join tableB tb on ta.id = tb.id inner join tableC tc on tb.col1 = tc.col2 where tc.col2 = 6 and tb.is_deleted = 'N' group by type_id union all -- 第二部分:原查询2的全局未读总数 select null as type, count(distinct ta.id) as count, 'unread_total' as statType from tableA ta inner join tableB tb on ta.id = tb.id inner join tableC tc on tb.col1 = tc.col2 where tc.col2 = 6 and tb.is_deleted = 'N' and tb.is_read = 'N'
statType为all_group的行是按type_id分组的统计数据;statType为unread_total的行是全局未读总数,type字段为null用于占位,保证两边查询列数、类型一致;- HQL完全支持
union all,只要两边查询的列匹配即可。
HQL适配注意点
- 两种方案的语法都兼容HQL,子查询、
case when、union all在主流Hibernate版本中均可正常运行; - 若使用较老的HQL版本,
cross join可替换为无on条件的join,效果一致。
内容的提问来源于stack exchange,提问作者blueSky
相关产品推荐
相关产品推荐

