MySQL同表连接生成多列(反规范化):汽水多口味平表查询
解决方案:将关联表反规范化为平表
没问题,这个需求属于典型的行转列场景,结合窗口函数就能轻松搞定。我给你两种通用的实现思路,适配大多数主流数据库:
通用条件聚合方案(适配MySQL/PostgreSQL/SQL Server等)
这种写法兼容性最强,不用依赖数据库特定的PIVOT语法:
SELECT s.idSoda, s.sodaName, MAX(CASE WHEN rn = 1 THEN f.FlavorText END) AS "FlavorText(first occurrence)", MAX(CASE WHEN rn = 2 THEN f.FlavorText END) AS "FlavorText(second occurrence)", MAX(CASE WHEN rn = 3 THEN f.FlavorText END) AS "FlavorText(third occurrence)" FROM Soda s LEFT JOIN SodaFlavorAsc sa ON s.idSoda = sa.SodaID LEFT JOIN Flavor f ON sa.FlavorID = f.idFlavor LEFT JOIN ( -- 给每个汽水的口味分配序号,这里按FlavorID排序,可按需调整 SELECT SodaID, FlavorID, ROW_NUMBER() OVER (PARTITION BY SodaID ORDER BY FlavorID) AS rn FROM SodaFlavorAsc ) ranked ON sa.SodaID = ranked.SodaID AND sa.FlavorID = ranked.FlavorID GROUP BY s.idSoda, s.sodaName ORDER BY s.idSoda;
代码细节解释:
- 子查询
ranked:用ROW_NUMBER()窗口函数按SodaID分组,给每个汽水关联的口味分配1、2、3...的序号。如果想按口味名称排序,把ORDER BY FlavorID改成ORDER BY FlavorText即可。 - 左连接关联:用
LEFT JOIN保证即使汽水没有关联口味,也能出现在结果中(对应口味列会显示NULL)。 - 条件聚合转列:通过
MAX(CASE...)组合,把序号为1、2、3的口味分别提取到单独列中。用MAX是因为分组后每个序号只会对应一个有效口味值,能自动过滤掉NULL。
数据库专属PIVOT方案(适用于SQL Server/Oracle)
如果你的数据库支持PIVOT语法,可以用更简洁的写法:
SELECT idSoda, sodaName, [1] AS "FlavorText(first occurrence)", [2] AS "FlavorText(second occurrence)", [3] AS "FlavorText(third occurrence)" FROM ( SELECT s.idSoda, s.sodaName, f.FlavorText, ROW_NUMBER() OVER (PARTITION BY s.idSoda ORDER BY f.idFlavor) AS rn FROM Soda s LEFT JOIN SodaFlavorAsc sa ON s.idSoda = sa.SodaID LEFT JOIN Flavor f ON sa.FlavorID = f.idFlavor ) AS source PIVOT ( MAX(FlavorText) FOR rn IN ([1], [2], [3]) ) AS pivot_table ORDER BY idSoda;
注意事项:
- 两种方案都会自动处理“不足3种口味”的情况:缺的口味列会显示
NULL,完全符合需求。 - 口味的顺序完全由
ROW_NUMBER()里的ORDER BY字段决定,可根据业务需求灵活调整。
内容的提问来源于stack exchange,提问作者J. G.
相关产品推荐
相关产品推荐

