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

使用Criteria Builder实现金额求和与GROUP_CONCAT报错排查

问题解决:GROUP_CONCAT嵌套聚合函数导致的"Invalid use of group function"错误

问题场景

需展示分组ID对应的日期-总价求和键值对(单日仅一条数据显示原值,多条则求和),使用Criteria Builder的sum、concat和GROUP_CONCAT方法时触发SQL语法错误。

错误信息

org.hibernate.exception.GenericJDBCException: 执行SQL时发生JDBC异常 [select gs1_0.storage_location_name,concat('[',concat(GROUP_CONCAT(concat(concat('{"x": "',concat(g1_0.grnCreatedAt,'", "y": ')),concat(cast(sum(i1_0.totalAmount) as char),'}'))),']')),cast(sum(i1_0.totalAmount) as char) from erp_grn g1_0 join erp_grn_goods i1_0 on g1_0.grnId=i1_0.grnId join erp_storage_location gs1_0 on gs1_0.storage_location_id=g1_0.functional_area where g1_0.companyId=? and g1_0.branchId=? and g1_0.locationId=? and g1_0.grnCreatedAt between ? and ? and g1_0.grnType=? group by gs1_0.storage_location_code,1 order by 3 desc limit ?] [无效使用分组函数] [无额外信息]

原实现代码

Expression<String> grnCreatedAt = root.get("grnCreatedAt");
Expression<BigDecimal> itemPriceSum = criteriaBuilder.sum(grnItemJoin.get("totalAmount"));
Expression<String> itemPriceSumAsString = itemPriceSum.as(String.class);
// Construct the JSON structure
Expression<String> dataJson = criteriaBuilder.concat(criteriaBuilder.literal("{\"x\": \""),
         criteriaBuilder.concat(grnCreatedAt, criteriaBuilder.literal("\", \"y\": ")));
dataJson = criteriaBuilder.concat(dataJson,
               criteriaBuilder.concat(itemPriceSumAsString, criteriaBuilder.literal("}")));
Expression<String> groupConcat = criteriaBuilder.function("GROUP_CONCAT", String.class, dataJson);
Expression<String> data = criteriaBuilder.concat(criteriaBuilder.literal("["),
                    criteriaBuilder.concat(groupConcat, criteriaBuilder.literal("]")));


if (bumpRequest.getType().equalsIgnoreCase("grn")) {
               query.multiselect(grnStorageName.alias("grnStorageName"), data.alias("data"),
                    itemPriceSumAsString.alias("item_price"));
} else {
               query.multiselect(grnCostCenterDesc.alias("grnCostCenterDesc"), data.alias("data"),
                    itemPriceSumAsString.alias("item_price"));}

           query.where(criteriaBuilder.and(companyPredicate, branchPredicate, locationPredicate, datePredicate,
                    grnTypePredicate));
if (bumpRequest.getType.equalsIgnoreCase("grn")) {
               query.groupBy(groupByField, grnStorageName);
           } else {
               query.groupBy(groupByField, grnCostCenterDesc);
           }
           query.orderBy(criteriaBuilder.desc(itemPriceSumAsString));

错误原因

SQL语法不允许在分组聚合函数(GROUP_CONCAT)内部直接嵌套另一个聚合函数(sum)。原代码试图在GROUP_CONCAT中直接使用sum计算每日总价,违反了SQL的分组函数使用规则。

修正方案

先通过子查询完成按日期+分组字段的每日总价聚合,生成日期-总价的JSON字符串;再在主查询中对每个分组字段,GROUP_CONCAT子查询得到的所有每日数据,避免聚合函数嵌套。

修正后代码示例

// 1. 构建子查询:按日期和分组字段聚合每日总价
Subquery<Tuple> subQuery = query.subquery(Tuple.class);
Root<Grn> subRoot = subQuery.from(Grn.class);
Join<Grn, GrnGoods> subItemJoin = subRoot.join("grnGoods");
Expression<String> subDate = subRoot.get("grnCreatedAt");
Expression<BigDecimal> subDailySum = criteriaBuilder.sum(subItemJoin.get("totalAmount"));

// 生成单条日期-总价JSON
Expression<String> dailyJson = criteriaBuilder.concat(
    criteriaBuilder.literal("{\"x\": \""),
    criteriaBuilder.concat(subDate, criteriaBuilder.concat("\", \"y\": ", subDailySum.as(String.class), "}"))
);

subQuery.multiselect(
    subRoot.get(groupByField).alias("groupField"),
    dailyJson.alias("dailyData")
);
subQuery.where(criteriaBuilder.and(companyPredicate, branchPredicate, locationPredicate, datePredicate, grnTypePredicate));
subQuery.groupBy(subRoot.get(groupByField), subDate);

// 2. 构建主查询:按分组字段GROUP_CONCAT所有每日数据
Root<Grn> mainRoot = query.from(Grn.class);
Expression<String> groupField;
if (bumpRequest.getType().equalsIgnoreCase("grn")) {
    Join<Grn, StorageLocation> storageJoin = mainRoot.join("functionalArea");
    groupField = storageJoin.get("storageLocationName").alias("grnStorageName");
} else {
    groupField = mainRoot.get("grnCostCenterDesc").alias("grnCostCenterDesc");
}

// 聚合每日JSON为数组格式
Expression<String> groupConcatData = criteriaBuilder.function(
    "GROUP_CONCAT", String.class, subQuery.getSelection().get("dailyData")
);
Expression<String> data = criteriaBuilder.concat("[", criteriaBuilder.concat(groupConcatData, "]"));

// 计算分组总金额
Expression<BigDecimal> totalSum = criteriaBuilder.sum(mainRoot.join("grnGoods").get("totalAmount"));
Expression<String> totalSumStr = totalSum.as(String.class);

// 设置查询选择项与分组规则
if (bumpRequest.getType().equalsIgnoreCase("grn")) {
    query.multiselect(groupField, data.alias("data"), totalSumStr.alias("item_price"));
    query.groupBy(mainRoot.get(groupByField), groupField);
} else {
    query.multiselect(groupField, data.alias("data"), totalSumStr.alias("item_price"));
    query.groupBy(mainRoot.get(groupByField), groupField);
}

query.where(criteriaBuilder.and(companyPredicate, branchPredicate, locationPredicate, datePredicate, grnTypePredicate));
query.orderBy(criteriaBuilder.desc(totalSumStr));

说明

  • 子查询先完成按日期的聚合,确保sum函数在正确的分组层级(日期+分组字段)中执行
  • 主查询仅负责对分组字段聚合,将每日JSON字符串拼接为数组格式,避免了聚合函数嵌套的语法错误
  • 最终结果满足需求:单日数据显示对应总价,多日数据按日期汇总后展示

内容的提问来源于stack exchange,提问作者Sayan Das

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 11:19:51