如何将PostgreSQL多行查询结果转换为单行多列?
需求可行,两种实现方法如下
方法一:条件聚合(新手友好,无需额外扩展)
这种方法通过CASE WHEN结合聚合函数,把不同座位等级的占比结果映射到单独的列中,逻辑直观,适合SQL新手理解。
完整SQL代码
SELECT datum, naam, -- 根据实际seatrank值调整条件,对应第一个座位等级的占比 MAX(CASE WHEN seatrank = 1 THEN rangen END) AS rangen_rank1, -- 对应第二个座位等级 MAX(CASE WHEN seatrank = 2 THEN rangen END) AS rangen_rank2, -- 对应第三个座位等级 MAX(CASE WHEN seatrank = 3 THEN rangen END) AS rangen_rank3, -- 对应第四个座位等级 MAX(CASE WHEN seatrank = 4 THEN rangen END) AS rangen_rank4 FROM ( SELECT sr.namenl rang, COUNT(CASE WHEN t.currentstatus IS NULL THEN '1' END) AS beschikbaar, COUNT(CASE WHEN t.currentstatus = 101 THEN '2' END) AS verkocht, COALESCE(e.namenl) || ' ' || COALESCE(e.subtitlenl,'') naam, e.startts datum, sr.id seatrank, -- 提前在子查询计算占比,简化外层逻辑 TO_CHAR(verkocht::NUMERIC/(verkocht+beschikbaar)*100, 'fm00D00%') rangen FROM tm.ticket t INNER JOIN tm.tickettype tt ON tt.id = t.tickettypeid INNER JOIN tm.event e ON e.id = tt.eventid INNER JOIN tm.seatrank sr ON sr.id = tt.seatrankid WHERE e.id = 28744 GROUP BY 1,4,5,6 ) sq1 GROUP BY datum, naam;
关键说明
- 你需要根据实际的
seatrank值(即座位等级的ID)修改CASE WHEN里的判断条件,比如如果你的四个座位等级ID是5、6、7、8,就把seatrank = 1改成对应的数字。 - 使用
MAX()是因为每个datum+naam+seatrank组合只有一行数据,MAX只是用来提取唯一的占比值,换成MIN()也能达到同样效果。
方法二:PostgreSQL专用交叉表(crosstab)
如果你的PostgreSQL环境允许启用扩展,crosstab函数是更简洁的转列方式,适合固定数量类别的场景。
步骤1:启用tablefunc扩展(仅需执行一次)
CREATE EXTENSION IF NOT EXISTS tablefunc;
步骤2:编写交叉表查询
SELECT * FROM crosstab( -- 源查询:返回分组键(datum、naam)、类别键(seatrank)、值(rangen) 'SELECT datum, naam, seatrank, rangen FROM ( SELECT sr.namenl rang, COUNT(CASE WHEN t.currentstatus IS NULL THEN ''1'' END) AS beschikbaar, COUNT(CASE WHEN t.currentstatus = 101 THEN ''2'' END) AS verkocht, COALESCE(e.namenl) || '' '' || COALESCE(e.subtitlenl,'''') naam, e.startts datum, sr.id seatrank, TO_CHAR(verkocht::NUMERIC/(verkocht+beschikbaar)*100, ''fm00D00%'') rangen FROM tm.ticket t INNER JOIN tm.tickettype tt ON tt.id = t.tickettypeid INNER JOIN tm.event e ON e.id = tt.eventid INNER JOIN tm.seatrank sr ON sr.id = tt.seatrankid WHERE e.id = 28744 GROUP BY 1,4,5,6 ) sq1 ORDER BY 1,2,3', -- 指定要生成列的类别值(自动获取当前事件的所有座位等级ID) 'SELECT DISTINCT seatrank FROM tm.seatrank WHERE id IN (SELECT seatrankid FROM tm.tickettype WHERE eventid=28744) ORDER BY id' ) AS ct( datum TIMESTAMP, naam TEXT, rangen_rank1 TEXT, rangen_rank2 TEXT, rangen_rank3 TEXT, rangen_rank4 TEXT );
关键说明
- 源查询必须严格按照「分组键 → 类别键 → 目标值」的顺序返回列。
- 结果表定义
AS ct(...)里的列名和类型要和实际数据匹配,列数要等于分组键数量加类别数量(这里是2个分组键+4个类别=6列)。
内容的提问来源于stack exchange,提问作者taxast
相关产品推荐
相关产品推荐

