一对多关联场景下多表连接时的多行合并实现方案咨询
一对多关联场景下多表连接时的多行合并实现方案咨询
嘿,我来帮你搞定这个一对多关联后合并多行的问题~先把你给出的三个表整理成清晰的表格,方便咱们对着分析:
一、表结构与样例数据
1. Item表
| Id | Name | Description |
|---|---|---|
| 1 | Test Name | Test Description |
| 2 | Other Name | Other Description |
| 3 | Another Name | Another Description |
2. Sales Price表
| Item Id | Unit Price |
|---|---|
| 1 | 9.99 |
| 2 | 12.99 |
| 3 | 15.99 |
3. Item Category表(一对多关联:一个商品对应多个分类)
我补全了点样例数据让场景更完整:
| Item Id | Category Id |
|---|---|
| 1 | 1 |
| 1 | 2 |
| 2 | 3 |
| 3 | 4 |
| 3 | 5 |
二、问题场景
咱们直接把三个表JOIN查询的话,因为Item和Item Category是一对多关系,会出现同一个商品对应多条记录(每个分类占一行),比如普通JOIN后的结果会是这样:
| Id | Name | Description | Unit Price | Category Id |
|---|---|---|---|---|
| 1 | Test Name | Test Description | 9.99 | 1 |
| 1 | Test Name | Test Description | 9.99 | 2 |
| 2 | Other Name | Other Description | 12.99 | 3 |
| 3 | Another Name | Another Description | 15.99 | 4 |
| 3 | Another Name | Another Description | 15.99 | 5 |
而咱们想要的是把同一个商品的多个分类合并成一行,比如Category列显示「1, 2」「4, 5」这种形式,对吧?下面分不同数据库给你具体的实现代码:
三、分数据库实现方案
1. MySQL / MariaDB
用GROUP_CONCAT()这个聚合函数就能轻松搞定,先关联所有表,再按商品维度分组合并分类:
SELECT i.Id, i.Name, i.Description, sp.`Unit Price`, -- 可以加ORDER BY让分类有序,加DISTINCT去重(如果有重复分类的话) GROUP_CONCAT(ic.`Category Id` ORDER BY ic.`Category Id` SEPARATOR ', ') AS `Category Ids` FROM Item i -- 关联价格表 JOIN `Sales Price` sp ON i.Id = sp.`Item Id` -- 关联分类表,要是商品可能没分类就改成LEFT JOIN JOIN `Item Category` ic ON i.Id = ic.`Item Id` GROUP BY i.Id, i.Name, i.Description, sp.`Unit Price`;
2. PostgreSQL
用STRING_AGG()函数,语法和MySQL类似,注意要把数字类型的Category Id转成文本:
SELECT i.Id, i.Name, i.Description, sp."Unit Price", STRING_AGG(ic."Category Id"::TEXT, ', ' ORDER BY ic."Category Id") AS "Category Ids" FROM Item i JOIN "Sales Price" sp ON i.Id = sp."Item Id" JOIN "Item Category" ic ON i.Id = ic."Item Id" GROUP BY i.Id, i.Name, i.Description, sp."Unit Price";
3. SQL Server
- 2017及以后版本:直接支持
STRING_AGG(),用法很直观:
SELECT i.Id, i.Name, i.Description, sp.[Unit Price], STRING_AGG(ic.[Category Id], ', ') WITHIN GROUP (ORDER BY ic.[Category Id]) AS [Category Ids] FROM Item i JOIN [Sales Price] sp ON i.Id = sp.[Item Id] JOIN [Item Category] ic ON i.Id = ic.[Item Id] GROUP BY i.Id, i.Name, i.Description, sp.[Unit Price];
- 2016及以前版本:得用
STUFF+FOR XML PATH的经典组合来模拟合并:
SELECT i.Id, i.Name, i.Description, sp.[Unit Price], STUFF( -- 子查询拼接分类 (SELECT ', ' + CAST(ic2.[Category Id] AS VARCHAR) FROM [Item Category] ic2 WHERE ic2.[Item Id] = i.Id ORDER BY ic2.[Category Id] FOR XML PATH(''), TYPE).value('.', 'VARCHAR(MAX)'), 1, 2, '' -- 去掉开头多余的「, 」 ) AS [Category Ids] FROM Item i JOIN [Sales Price] sp ON i.Id = sp.[Item Id] -- 这里如果要包含无分类商品,就不用分组,或者LEFT JOIN后处理 GROUP BY i.Id, i.Name, i.Description, sp.[Unit Price];
4. Oracle
用LISTAGG()函数,还能直接指定合并后的排序:
SELECT i.Id, i.Name, i.Description, sp."Unit Price", LISTAGG(ic."Category Id", ', ') WITHIN GROUP (ORDER BY ic."Category Id") AS "Category Ids" FROM Item i JOIN "Sales Price" sp ON i.Id = sp."Item Id" JOIN "Item Category" ic ON i.Id = ic."Item Id" GROUP BY i.Id, i.Name, i.Description, sp."Unit Price";
四、小提示
- 如果你的商品可能存在没有分类的情况,记得把
JOIN改成LEFT JOIN,不然无分类的商品会被过滤掉; - 所有聚合函数里的
ORDER BY都是可选的,加了之后合并的分类会按从小到大排列,看起来更规整; - 要是分类表可能出现重复的
Item Id + Category Id组合,可以在聚合函数里加DISTINCT(比如MySQL的GROUP_CONCAT(DISTINCT ic.Category Id...))来避免合并后出现重复的分类值。
这样应该就很清晰啦,你可以根据自己用的数据库直接抄代码改改就能用~




