通过选择查询转置表

时间:2010-12-10 12:08:42

标签: sql oracle pivot

我有一张表:

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查询完成此操作。案例陈述,解码??

4 个答案:

答案 0 :(得分:3)

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

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

它以11g的形式添加到了Oracle中。请注意,您需要在pivot子句中指定结果列(来自unpivoted列的值,这些值将成为透视列名称)。未在透视中指定的任何列都隐式分组。如果原始表中有您不希望分组的列,请从视图或子查询中选择,而不是从表中选择。

您可以参与一些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技术(根据为每个数据透视值明确定义单独的列,生成其他人发布的查询类型)。

答案 1 :(得分:1)

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;

答案 2 :(得分:0)

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

答案 3 :(得分:0)

如果您无权访问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的答案最适合您的需求,重新查看您的样本结果。