2010-12-10 225 views
2

换位表我有一个表,如:通过选择查询

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

回答

3

你正在寻找所谓的“pivot”(也称“Pivoting Operations”看到Oracle数据库数据仓库指南):

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

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

您可以参与一点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); 

你可以走的更远,定义一个函数,它接受一个表名,合计表达和枢轴列名,通过第一p返回一个支点声明然后再评估上述说法。然后,您可以定义一个采用相同参数并生成旋转结果的过程。我没有访问到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注入的影响。

在pre-11g中,您可以应用MySQL pivot statement generation技术(根据显式定义每个枢轴值的单独列来生成他人发布的查询类型)。

+2

它适用于Oracle 11,但对于早期版本,方法在此处提供http://www.orafaq.com/wiki/PIVOT – 2010-12-10 14:20:01

0

从来没有尝试过,但似乎至少甲骨文11具有枢条款

1

枢轴确实大大简化事情。但是,在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; 
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的回答会在重新查看您的样本结果后,最符合您的需求。