将一对多关联的SQL查询结果展平为单行
实现SQL行转列:将同一预约的物品转为独立字段
你需要将Appt表与SuppliedGoods表关联后,把每个预约下的多个物品转为独立的列,每个预约仅显示一行,而非多行结果。以下是两种可行的解决方案:
方法1:条件聚合(通用SQL方言)
这是兼容性最好的方案,适用于几乎所有支持窗口函数的数据库(MySQL 8.0+、PostgreSQL、SQL Server、Oracle等)。
核心逻辑:
- 给每个预约下的物品生成唯一序号,区分第1、第2、第3个物品
- 通过
CASE语句结合聚合函数MAX,将不同序号的物品映射到独立列
SELECT a.ApptID, a.ApptDate, MAX(CASE WHEN sg.ItemSeq = 1 THEN sg.SuppliedGoodsName END) AS SuppliedGoodsName1, MAX(CASE WHEN sg.ItemSeq = 2 THEN sg.SuppliedGoodsName END) AS SuppliedGoodsName2, MAX(CASE WHEN sg.ItemSeq = 3 THEN sg.SuppliedGoodsName END) AS SuppliedGoodsName3 FROM Appt a LEFT JOIN ( -- 子查询:给每个ApptID下的物品按ID排序并生成序号 SELECT ApptID, SuppliedGoodsName, ROW_NUMBER() OVER (PARTITION BY ApptID ORDER BY ID) AS ItemSeq FROM SuppliedGoods ) sg ON a.ApptID = sg.ApptID GROUP BY a.ApptID, a.ApptDate ORDER BY a.ApptID;
结果说明:
- 每个预约对应一行数据
- 物品不足3个的位置会显示
NULL(如ApptID=2的第3列) - 列名使用
SuppliedGoodsName1/2/3避免重复(数据库不允许同名列)
方法2:使用PIVOT语法(部分数据库支持)
如果你的数据库支持PIVOT(如SQL Server、Oracle),可以用更简洁的语法实现:
SELECT ApptID, ApptDate, [1] AS SuppliedGoodsName1, [2] AS SuppliedGoodsName2, [3] AS SuppliedGoodsName3 FROM ( -- 源数据:关联两表并生成物品序号 SELECT a.ApptID, a.ApptDate, sg.SuppliedGoodsName, ROW_NUMBER() OVER (PARTITION BY a.ApptID ORDER BY sg.ID) AS ItemSeq FROM Appt a LEFT JOIN SuppliedGoods sg ON a.ApptID = sg.ApptID ) AS SourceTable PIVOT ( MAX(SuppliedGoodsName) FOR ItemSeq IN ([1], [2], [3]) ) AS PivotTable ORDER BY ApptID;
为什么之前的GROUP BY失败?
你之前直接GROUP BY时失败,是因为SuppliedGoodsName没有被包含在聚合函数中。每个预约分组下对应多个物品名称,数据库无法自动确定要保留哪个值。而上面的方案中,CASE+MAX本质是一种聚合逻辑:每个序号只对应一个物品名称,MAX会保留该值、忽略其他NULL,从而实现按序号拆分列。
内容的提问来源于stack exchange,提问作者GaWill
相关产品推荐
相关产品推荐

