在 Oracle 中使用动态列进行透视

Posted

技术标签:

【中文标题】在 Oracle 中使用动态列进行透视【英文标题】:Pivot with Dynamic Columns in Oracle 【发布时间】:2020-09-30 12:22:03 【问题描述】:

我是新的 oracle 数据库,我有一个如下表

ID     Passengers Age Eligible
123456 Ben        65  Yes
123456 Mary       58  Yes
123458 Stephanie  37  Yes
123458 Aaron      32  Yes
123458 Caroline   18  No

我想得到动态列名为 Age1、Age2、Age3 等的结果,如下所示

ID      Age1 Age2 Age3
123456  65   58   NULL
123458  37   32   18

这可以通过SQL Server 中的 STUFF 和 Dynamic Pivot 来实现,但我不知道如何在Oracle 中实现这一点

谁能指导我如何在Oracle动态地做到这一点

【问题讨论】:

SO中有很多例子,其中一个有多种方式是***.com/a/31420487/13878023..你检查并卡在某个地方了吗?另一个简单的,你也可以关注...***.com/questions/50259728/oracle-dynamic-pivoting @Sujitmohanty30 我已经解决了您在此处提到的问题,动态列名都来自列值,但我希望将相同的列名拆分为三个,例如。年龄为 Age1,Age2,Age3,因为我是 Oracle 的新手,所以在 Oracle 中这样做对我来说很困难。我也希望结果集为表格 我明白了,这没问题。一个问题是为什么结果中缺少Passengers?由于时间限制,我只能提供查询,然后可以将其转换为PLSQL。或者只是等待,我相信你很快就会得到解决方案:) 【参考方案1】:

SYS_REFCURSOR 在存储函数中(例如使用 PL/SQL 而不是直接使用 SQL)可能用于获取动态生成的结果集(例如。Dynamic Pivot)。在这种情况下,会生成一个用于条件聚合的字符串:

CREATE OR REPLACE FUNCTION get_passengers_rs RETURN SYS_REFCURSOR IS
  v_recordset SYS_REFCURSOR;
  v_sql       VARCHAR2(32767);
  v_str       VARCHAR2(32767);
BEGIN
  SELECT LISTAGG('MAX(CASE WHEN rn = '||lvl||' THEN age||''(''||passengers||'')'' END) 
                   AS "Age'||lvl||'"' ,',') WITHIN GROUP (ORDER BY 0)
    INTO v_str
    FROM ( SELECT level AS lvl  
             FROM dual
          CONNECT BY level <= (SELECT MAX(COUNT(*)) FROM t GROUP BY ID ) ) t;  

  v_sql :=
  'SELECT ID, '|| v_str ||'
     FROM 
     (
      SELECT t.*, 
             ROW_NUMBER() OVER (PARTITION BY ID ORDER BY 0) AS rn
        FROM t     
      )
    GROUP BY ID';

  OPEN v_recordset FOR v_sql;
  RETURN v_recordset;
END;

我还添加了乘客的姓名,以便很好地区分每个数据。

然后运行以下代码:

VAR rc REFCURSOR
EXEC :rc := get_passengers_rs;
PRINT rc

从 SQL Developer 的命令行查看预期的结果集。

以上代码为当前存在的数据生成这个SQL字符串(v_sql)

SELECT ID, MAX(CASE WHEN rn = 1 THEN age||'('||passengers||')' END) AS "Age1",
           MAX(CASE WHEN rn = 2 THEN age||'('||passengers||')' END) AS "Age2",
           MAX(CASE WHEN rn = 3 THEN age||'('||passengers||')' END) AS "Age3"
  FROM 
     (
      SELECT t.*, 
             ROW_NUMBER() OVER (PARTITION BY ID ORDER BY 0) AS rn
        FROM t     
      )
 GROUP BY ID

产生

ID      Age1        Age2            Age3
123456  58(Marie)   65(Ben) 
123458  32(Aaron)   18(Caroline)    37(Stephanie)

作为结果集。

【讨论】:

以上是关于在 Oracle 中使用动态列进行透视的主要内容,如果未能解决你的问题,请参考以下文章

使用 Sql Developer Oracle 的动态数据透视查询

Oracle 动态透视

如何在动态数据透视中先按行排序,然后按列排序

SQL 动态数据透视表列顺序

如何在一个列上对 sql server 进行透视,但重命名为动态列

用于动态 sql 透视的 Oracle 函数