You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多
文档控制台
免费开始使用

一对多关联场景下多表连接时的多行合并实现方案咨询

一对多关联场景下多表连接时的多行合并实现方案咨询

嘿,我来帮你搞定这个一对多关联后合并多行的问题~先把你给出的三个表整理成清晰的表格,方便咱们对着分析:

一、表结构与样例数据

1. Item表

IdNameDescription
1Test NameTest Description
2Other NameOther Description
3Another NameAnother Description

2. Sales Price表

Item IdUnit Price
19.99
212.99
315.99

3. Item Category表(一对多关联:一个商品对应多个分类)

我补全了点样例数据让场景更完整:

Item IdCategory Id
11
12
23
34
35

二、问题场景

咱们直接把三个表JOIN查询的话,因为Item和Item Category是一对多关系,会出现同一个商品对应多条记录(每个分类占一行),比如普通JOIN后的结果会是这样:

IdNameDescriptionUnit PriceCategory Id
1Test NameTest Description9.991
1Test NameTest Description9.992
2Other NameOther Description12.993
3Another NameAnother Description15.994
3Another NameAnother Description15.995

而咱们想要的是把同一个商品的多个分类合并成一行,比如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";

四、小提示

  1. 如果你的商品可能存在没有分类的情况,记得把JOIN改成LEFT JOIN,不然无分类的商品会被过滤掉;
  2. 所有聚合函数里的ORDER BY都是可选的,加了之后合并的分类会按从小到大排列,看起来更规整;
  3. 要是分类表可能出现重复的Item Id + Category Id组合,可以在聚合函数里加DISTINCT(比如MySQL的GROUP_CONCAT(DISTINCT ic.Category Id ...))来避免合并后出现重复的分类值。

这样应该就很清晰啦,你可以根据自己用的数据库直接抄代码改改就能用~

火山引擎 最新活动