【问题标题】:Transposing a table through select query通过选择查询转置表
【发布时间】:2011-05-23 11:04:31
【问题描述】:

我有一张这样的桌子:

Key  type      value
---------------------
40     A        12.34    
41     A        10.24    
41     B        12.89

我想要它的格式:

Types     40    41     42 (keys)
---------------------------------
A       12.34   10.24  XXX    
B       YYY     12.89  ZZZ

如何通过 SQL 查询来完成。案例陈述,解码??

【问题讨论】:

    标签: sql oracle pivot


    【解决方案1】:

    您要查找的内容称为“pivot”(另请参见 Oracle 数据库数据仓库指南中的“Pivoting Operations”):

    SELECT * 
      FROM tbl
      PIVOT(SUM(value) FOR Key IN (40, 41, 42))
    

    它在 11g 中被添加到 Oracle。请注意,您需要在数据透视子句中指定结果列(未透视列中的值成为透视列名称)。任何未在数据透视表中指定的列都被隐式分组。如果您不想在原始表中进行分组,请从视图或子查询中进行选择,而不是从表中进行选择。

    您可以参与一点wizardry 并让 Oracle 为您创建语句,这样您就无需弄清楚要以哪些列值为轴心。在 11g 中,当您知道列值是数字时:

    SELECT
        'SELECT * FROM tbl PIVOT(SUM(value) FOR Key IN ('
        || LISTAGG(Key, ',') WITHIN GROUP (ORDER BY Key)
        || ');'
      FROM tbl;
    

    如果列值可能不是数字:

    SELECT
        'SELECT * FROM tbl PIVOT(SUM(value) FOR Key IN (\''
        || LISTAGG(Key, '\',\'') WITHIN GROUP (ORDER BY Key)
        || '\'));'
      FROM tbl;
    

    LISTAGG 可能重复重复(有人会测试这个吗?),在这种情况下你需要:

    SELECT
        'SELECT * FROM tbl PIVOT(SUM(value) FOR Key IN (\''
        || LISTAGG(Key, '\',\'') WITHIN GROUP (ORDER BY Key)
        || '\'));'
      FROM (SELECT DISTINCT Key FROM tbl);
    

    您可以更进一步,定义一个函数,该函数接受一个表名、聚合表达式和枢轴列名,该函数通过首先生成然后评估上述语句来返回一个枢轴语句。然后,您可以定义一个采用相同参数并产生旋转结果的过程。我无法访问 Oracle 11g 来测试它,但我相信它看起来像:

    CREATE PACKAGE dynamic_pivot AS
      -- creates a PIVOT statement dynamically
      FUNCTION pivot_stmt (tbl_name IN varchar2(30),
                           pivot_col IN varchar2(30), 
                           aggr IN varchar2(40), 
                           quote_values IN BOOLEAN DEFAULT TRUE)
          RETURN varchar2(300);
      PRAGMA RESTRICT_REFERENCES (pivot_stmt, WNDS, RNPS);
    
      -- creates & executes a PIVOT
      PROCEDURE pivot_table (tbl_name IN varchar2(30),
                             pivot_col IN varchar2(30), 
                             aggr IN varchar2(40), 
                             quote_values IN BOOLEAN DEFAULT TRUE);
    END dynamic_pivot;
    
    CREATE PACKAGE BODY dynamic_pivot AS
      FUNCTION pivot_stmt (
          tbl_name IN varchar2(30), 
          pivot_col IN varchar2(30), 
          aggr_expr IN varchar2(40), 
          quote_values IN BOOLEAN DEFAULT TRUE
        ) RETURN varchar2(300)
      IS
         stmt VARCHAR2(400);
         quote VARCHAR2(2) DEFAULT '';
      BEGIN
         IF quote_values THEN
           quote :=  '\\\'';
         END IF;
         -- "\||" shows that you are still in the dynamic statement string
         -- The input fields aren't sanitized, so this is vulnerable to injection
         EXECUTE IMMEDIATE 'SELECT \'SELECT * FROM ' || tbl_name 
                 || ' PIVOT(' || aggr_expr || ' FOR ' || pivot_col 
                 || ' IN (' || quote || '\' \|| LISTAGG(' || pivot_col
                            || ', \'' || quote || ',' || quote
                 || '\') WITHIN GROUP (ORDER BY ' || pivot_col || ') \|| \'' || quote 
                 || '));\' FROM (SELECT DISTINCT ' || pivot_col || ' FROM ' || tbl_name || ');'
           INTO stmt;
         RETURN stmt;
      END pivot_stmt;
    
      PROCEDURE pivot_table (tbl_name IN varchar2(30), pivot_col IN varchar2(30), aggr_expr IN varchar2(40), quote_values IN BOOLEAN DEFAULT TRUE) IS
      BEGIN
        EXECUTE IMMEDIATE pivot_stmt(tbl_name, pivot_col, aggr_expr, quote_values);
      END pivot_table;
    END dynamic_pivot;
    

    注意:tbl_namepivot_colaggr_expr参数的长度来自maximum table and column name length。另请注意,该函数易受 SQL 注入攻击。

    在 11g 之前的版本中,您可以应用 MySQL pivot statement generation 技术(根据为每个数据透视值显式定义单独的列,生成其他人发布的查询类型)。

    【讨论】:

    【解决方案2】:

    Pivot 确实大大简化了事情。但是在 11g 之前,您需要手动执行此操作。

    select 
      type, 
      sum(case when key = 40 then value end) as val_40,
      sum(case when key = 41 then value end) as val_41,
      sum(case when key = 42 then value end) as val_42
    from my_table
    group by type;
    

    【讨论】:

      【解决方案3】:

      从未尝试过,但似乎至少 Oracle 11 有 PIVOT 子句

      【讨论】:

        【解决方案4】:

        如果您无法访问 11g,则可以使用字符串聚合和分组方法来大约。你在找什么,比如

        with data as(
        SELECT 40 KEY , 'A' TYPE , 12.34 VALUE FROM DUAL UNION 
        SELECT 41 KEY , 'A' TYPE , 10.24 VALUE FROM DUAL UNION 
        SELECT 41 KEY , 'B' TYPE , 12.89 VALUE FROM DUAL 
        )
                  select   
                         TYPE ,                  
                         wm_concat(KEY) KEY ,
                        wm_concat(VALUE) VALUE 
                      from   data       
                GROUP BY TYPE;  
        
        type     KEY     VALUE
        ------ ------- -----------
        A       40,41   12.34,10.24
        B       41      12.89               
        

        这是基于 wm_concat 的,如下所示:http://www.oracle-base.com/articles/misc/StringAggregationTechniques.php

        我将把它留在这里以防万一,但我认为 PIVOT 或 MikeyByCrikey 的答案在重新查看您的示例结果后最适合您的需求。

        【讨论】:

          猜你喜欢
          • 2017-09-17
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-06-23
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多