使用 PIVOT 函数的行到列 (Oracle)

Posted

技术标签:

【中文标题】使用 PIVOT 函数的行到列 (Oracle)【英文标题】:Rows to columns using PIVOT function (Oracle) 【发布时间】:2020-10-16 13:41:17 【问题描述】:

我正在尝试创建一个查询以使用 PIVOT 函数将行转换为列。我有一个这种形式的表格(这只是部分视图,表格包含30多列)。

ID      SUBJECT  GRADE
000442  WRI001   C-
000442  PHY104   C
000442  MTH111   B
000442  MTH111   W
000442  MTH111   W
000442  PHY104   W

预期结果:

ID      'WRI001'   'MTH111'   'PHY104'
000442   C-         B,W,W      C,W

使用的查询:

select * from (
   select ID,
          SUBJECT,GRADE
   from SECOND_YEAR_COMP
             )
pivot 
(
   MAX(GRADE)
   for SUBJECT in
   ('MTH111',
    'WRI001',
    'PHY104')
);

查询输出:

ID      'WRI001'   'MTH111'   'PHY104'
000442   C-         W          W

我知道,因为 MAX(GRADE),我每门科目的成绩都只有一个。有什么办法可以得到该科目的所有成绩(正如我上面给出的预期结果)。

【问题讨论】:

您显示的输出不是查询的输出。这是因为输出中的列将完全遵循 PIVOT 子句的 IN 列表中的顺序(除非您在查询末尾有 ORDER BY,而您没有)。 【参考方案1】:

您可以使用listagg() 和条件聚合:

select id,
    listagg(case when subject = 'WRI001' then grade end) WRI001,
    listagg(case when subject = 'WRI001' then grade end) WRI001,
    listagg(case when subject = 'PHY104' then grade end) PHY104
from second_year_comp
group by id

您可以使用within group 子句控制成绩在连接字符串中出现的顺序。假设你想按等级排序,那么:

select id,
    listagg(case when subject = 'WRI001' then grade end) within group(order by grade) WRI001,
    listagg(case when subject = 'WRI001' then grade end) within group(order by grade) WRI001,
    listagg(case when subject = 'PHY104' then grade end) within group(order by grade) PHY104
from second_year_comp
group by id

【讨论】:

这是正确的 - 但既然 OP 已经在使用 PIVOT 运算符,为什么不在 PIVOT 中使用 LISTAGG 显示解决方案?【参考方案2】:

快完成了 - 唯一缺少的一点是 - 您应该使用 PIVOT 子句中的完整语法MAX 聚合函数替换为 LISTAGG 函数。

此外,我调整了数据透视列的名称以获得不错的名称(不带撇号)。

请看下面的例子:

select * from (
   select ID,
          SUBJECT,GRADE
   from tab
             )
pivot 
(
   LISTAGG(GRADE,',') within group (order by GRADE)
   for SUBJECT in
   ('MTH111' as MTH111,
    'WRI001' as WRI001,
    'PHY104' as PHY104)
)

结果符合预期

ID     MTH111     WRI001     PHY104    
------ ---------- ---------- ----------
000442 B,W,W      C-         C,W    

【讨论】:

几乎和预期的一样; OP 显示了不同的输出(正如我在他的问题下方的评论中解释的那样,这不是他与我们共享的查询的输出)。与输出中的列顺序有关 - 与 PIVOT 的 IN 列表中的顺序相同。【参考方案3】:

您可以使用listagg() 进行字符串聚合,然后应用pivot

With cte as
(
SELECT ID, SUBJECT,LISTAGG(GRADE, ',') WITHIN GROUP (ORDER BY grade) AS grade
FROM   tablename
GROUP BY id,SUBJECT
)
select * from (
   select ID,SUBJECT,GRADE from cte)
pivot 
(
   MAX(GRADE)
   for SUBJECT in ('MTH111','WRI001','PHY104')
);

【讨论】:

为什么聚合与透视分开?两者都是聚合操作; PIVOT 可以很好地处理 LISTAGG 作为聚合函数。我认为这不是一个好的答案(反映在投票中)。

以上是关于使用 PIVOT 函数的行到列 (Oracle)的主要内容,如果未能解决你的问题,请参考以下文章

SQL Pivot 行到列标题

使用 Pivot 在 MySQL 8.0.17 版中从不同表和不同行号(联合)进行行到列转换

在Oracle中按组不使用聚合函数或行到列

SQL 查询中的行到列

Oracle 行到列的转换

Spark:行到列(如转置或枢轴)