【问题标题】:Use pivot for dynamically changing column headers using sql in oracle在 oracle 中使用 sql 动态更改列标题
【发布时间】:2020-11-17 07:36:17
【问题描述】:

我有一个需要对数据进行透视的要求。 然而,枢轴需要是动态的,因为列标题会根据列 app_id 不断变化。 所以如果 app_id=1。列标题将为 A、B、C、D,如果 app_id=2,列将为 CDEF 等等。 此外,每组值都有一个 id。因此对于 id、120 和 app_id=1 ,A、B、C、D 列将显示值等等。

当前示例数据只有 2 个 app_id,但可能还有更多,所以 app_id 和标签会不断变化,因此我需要编写一个动态查询。

示例表数据:

ID  label   value   app_id
--- -----   -----   ------
120 A       Alpha   1
120 B       Beta    1
120 C       Class   1
120 D       Delta   1
120 C       Alpha   2
120 D       Beta    2
120 E       Class   2
120 F       Delta   2

建设性的查询是

WITH data( ID, label, value, app_id ) AS
(
  SELECT 120, 'A', 'Alpha', 1 FROM dual UNION ALL
  SELECT 120, 'B', 'Beta' , 1 FROM dual UNION ALL
  SELECT 120, 'C', 'Class', 1 FROM dual UNION ALL
  SELECT 120, 'D', 'Delta', 1 FROM dual UNION ALL
  SELECT 120, 'C', 'Alpha', 2 FROM dual UNION ALL
  SELECT 120, 'D', 'Beta' , 2 FROM dual UNION ALL
  SELECT 120, 'E', 'Class', 2 FROM dual UNION ALL
  SELECT 120, 'F', 'Delta', 2 FROM dual  
)
SELECT * 
  FROM data

预期输出:

SELECT * FROM data WHERE ID = 120 AND app_id = 1;    
app_id  A        B      C      D      ID
------  ------   -----  -----  -----  -----
1       Alpha    Beta   Class  Delta  120

SELECT * FROM data WHERE ID = 120 AND app_id = 2;    
app_id  C        D      E      F      ID
------  ------   -----  -----  -----  -----
2       Alpha    Beta   Class  Delta  120

【问题讨论】:

    标签: sql oracle plsql pivot


    【解决方案1】:

    你能做什么

    SELECT * 
      FROM data  
     PIVOT  
     (
      MAX(value) FOR label IN ('A' AS "A", 'B' AS "B",'C' AS "C",'D' AS "D")
     )
     WHERE ID = 120 AND app_id = 1
    

    作为静态数据透视语句可能会转换为包含两个相应参数的函数

    CREATE OR REPLACE FUNCTION Get_Pivoted_Labels( i_id data.id%type, i_app_id data.app_id%type ) 
    RETURN SYS_REFCURSOR IS
      v_recordset SYS_REFCURSOR;
      v_sql       VARCHAR2(32767); 
      v_cols      VARCHAR2(32767);  
    BEGIN
      SELECT LISTAGG( ''''||label||''' AS "'||label||'"' , ',' )
              WITHIN GROUP ( ORDER BY label ) 
        INTO v_cols
        FROM ( SELECT DISTINCT label 
                 FROM data
                WHERE ID = i_id AND app_id = i_app_id );
    
      v_sql :=
          'SELECT * 
             FROM data
            PIVOT 
            (
              MAX(value) FOR label IN ( '|| v_cols ||' )
            )
           WHERE ID = :id AND app_id = :aid'; 
    
      OPEN v_recordset FOR v_sql USING i_id, i_app_id;
      RETURN v_recordset;
    END;
    /
    

    其中一个辅助查询,其中明确选择了label 列,用于确定要连接到主 SQL 字符串的字符串(v_cols for 'A' AS "A", 'B' AS "B",'C' AS "C",'D' AS "D")以便在返回 SYS_REFCURSOR 类型值的游标。

    并被调用

    VAR rc REFCURSOR
    VAR v_id NUMBER
    VAR v_app_id NUMBER
    EXEC :rc := Get_Pivoted_Labels(:v_id,:v_app_id);
    PRINT rc
    

    来自 SQL 开发人员的控制台。

    Demonstration 带有生成的 SQL 语句

    如果 SELECT 列表中的列顺序很重要,请使用下面的代码来创建函数

    CREATE OR REPLACE FUNCTION Get_Pivoted_Labels( i_id data.id%type, i_app_id data.app_id%type ) 
    RETURN SYS_REFCURSOR IS
      v_recordset SYS_REFCURSOR;
      v_sql       VARCHAR2(32767); 
      v_cols_1    VARCHAR2(32767);    
      v_cols_2    VARCHAR2(32767);   
    BEGIN
      SELECT LISTAGG( ''''||label||''' AS "'||label||'"' , ',' )
              WITHIN GROUP ( ORDER BY label ),
             LISTAGG( label , ',' )
              WITHIN GROUP ( ORDER BY label )  
        INTO v_cols_1, v_cols_2
        FROM ( SELECT DISTINCT label, value 
                 FROM data
                WHERE ID = i_id AND app_id = i_app_id );
    
      v_sql :=
          'SELECT ID, '|| v_cols_2 ||', app_id
             FROM data
            PIVOT 
            (
              MAX(value) FOR label IN ( '|| v_cols_1 ||' )
            )
           WHERE ID = :id AND app_id = :aid'; 
    
      OPEN v_recordset FOR v_sql USING i_id, i_app_id;
      RETURN v_recordset;
    END;
    /
    

    【讨论】:

    • 正如 OP 在他的 cmets 中对我的回答所说,最终的“客户端”是 Oracle APEX,它是少数不能使用 REF CURSOR 的环境之一。
    • 感谢@StewAshton,但随后需要阅读每条评论,即使我自己的答案或问题下方不存在:)
    • 为什么是“但是”?我从没想过你会阅读对别人问题的每一条评论。这就是我花时间通知你的原因。为什么人们会像批评一样试图告知?
    • 好吧@StewAshton,您的通知没问题,而OP希望清楚地说明他在问题中需要什么。这样我们就不会在一些无用的问题上浪费时间。
    【解决方案2】:

    这是一个很常见的问题,原因很简单,答案是“否”。

    使用 Oracle 数据库,每个 SELECT 语句都必须具有固定且已知的“形状”(列数、名称和数据类型)。在最近的版本中,有“多态表函数”似乎打破了这个规则,但实际上它们并没有:“形状”是在解析语句时计算的,因此在执行开始之前它是固定的和已知的。

    您不想要“多态”(在解析时改变形状),您想要真正的“动态”(在执行时根据数据改变形状)。 Oracle 不这样做。

    您可以使用一条 SQL 语句获得的最接近的结果是准确输出包含 XML 或 JSON 的 一个 列。然后调用数据库的程序将负责将该结果转换为行和列。

    另一种选择是执行一个 SELECT 来获取列名并生成第二个 SELECT 来获得您想要的结果。我写了一个函数来帮助做到这一点: https://stewashton.wordpress.com/2018/05/30/improved-pivot-function/

    我不会演示这些替代方案,因为它们不会直接回答您的问题。您的问题没有直接答案。

    【讨论】:

    • 这可以使用数组/集合来完成吗?只是问,因为我对替代方案一无所知。
    • 您问“这可以使用数组/集合来完成吗?”我不明白你的意思,因为数组/集合也有固定的形状。首先告诉我什么软件在调用数据库:它是一个应用程序吗?是 SQL*Plus 吗?它是某种报告工具吗?可能的替代方案取决于您的答案。
    • SQL Developer-> Oracle 12.1
    • 不,我的意思是一旦你完成了开发,查询会发生什么?它将在哪里部署到生产环境中?那么什么程序会执行呢?
    • 此查询将用作 oracle apex 应用程序中的数据源
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-12-16
    • 1970-01-01
    • 1970-01-01
    • 2018-12-14
    • 1970-01-01
    • 2018-04-10
    相关资源
    最近更新 更多