2010-12-10 237 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的回答會在重新查看您的樣本結果後,最符合您的需求。