商品与类别多对多关联查询去重并聚合类别名的SQL问题
解决多对多关联查询中商品重复并聚合类别名称的问题
核心思路
通过分组聚合将同一商品的多个类别名称合并成集合或字符串,同时确保商品信息唯一返回。
具体实现方案
1. SQL层面直接处理(主流数据库示例)
MySQL环境
利用GROUP_CONCAT()聚合类别名称,配合GROUP BY按商品唯一字段分组:
SELECT p.id, p.name, p.price, GROUP_CONCAT(c.name SEPARATOR ', ') AS category_names FROM product p JOIN product_category pc ON p.id = pc.product_id JOIN category c ON pc.category_id = c.id WHERE p.name LIKE '%目标名称%' -- 替换为你的名称筛选条件 GROUP BY p.id, p.name, p.price; -- 按商品所有非聚合字段分组
- 说明:
GROUP_CONCAT()会把同一商品的所有类别名称用指定分隔符拼接成字符串;GROUP BY必须包含所有未被聚合的商品字段,避免语法错误。如果需要返回数组格式,MySQL 8.0+可改用JSON_ARRAYAGG(c.name)。
PostgreSQL环境
改用STRING_AGG()函数实现聚合:
SELECT p.id, p.name, p.price, STRING_AGG(c.name, ', ') AS category_names FROM product p JOIN product_category pc ON p.id = pc.product_id JOIN category c ON pc.category_id = c.id WHERE p.name LIKE '%目标名称%' GROUP BY p.id, p.name, p.price;
- 若需数组格式,可使用
ARRAY_AGG(c.name)。
2. 后端代码层处理(无法修改SQL时)
如果因业务限制不能调整SQL,可在接口逻辑中对原始查询结果去重合并,以Java伪代码为例:
// 假设原始查询返回的列表是List<ProductRaw>,包含商品+单个类别信息 Map<Long, ProductDto> productMap = new HashMap<>(); for (ProductRaw item : originalList) { ProductDto dto = productMap.get(item.getProductId()); if (dto == null) { dto = new ProductDto(); dto.setId(item.getProductId()); dto.setName(item.getProductName()); dto.setPrice(item.getPrice()); dto.setCategories(new ArrayList<>()); productMap.put(item.getProductId(), dto); } dto.getCategories().add(item.getCategoryName()); } // 最终结果转换为列表 List<ProductDto> resultList = new ArrayList<>(productMap.values());
关键注意事项
- SQL分组时,
GROUP BY的字段必须能唯一标识商品(通常是主键+其他必填字段),否则会导致商品信息丢失或错误合并。 - 聚合函数的分隔符可根据需求调整,比如用
;或换行符,适配前端展示需求。
内容的提问来源于stack exchange,提问作者Frentzen Chan
相关产品推荐
相关产品推荐

