[轉]decode函式和行列互換

jacksonkingdom發表於2010-04-19
http://tech.it168.com/a2010/0412/872/000000872348.shtml
   
Decode函式的語法結構如下:

    decode (expression, search_1, result_1)
    decode (expression, search_1, result_1, search_2, result_2)
    decode (expression, search_1, result_1, search_2, result_2, ....,
    search_n, result_n)
    decode (expression, search_1, result_1, default)
    decode (expression, search_1, result_1, search_2, result_2, default)
    decode (expression, search_1, result_1, search_2, result_2, ....,
    search_n, result_n, default)

    作用:和case表示式實現的功能一樣,以第1 個引數為關鍵欄位,如果等於search,則結果為對應的result,最後匹配不到的結果為default。如果沒有寫default,返回null。 為了格式一致,建議匹配不到不返回結果時,寫null。如果第一個返回結果為char 或null,oracle自動轉型為varchar2,如下面例子

    SELECT product_id,
    DECODE (warehouse_id, 1, ’Southlake’,
    2, ’San Francisco’,
    3, ’New Jersey’,
    4, ’Seattle’,
    ’Non-domestic’)
    "Location of inventory" FROM inventories
    WHERE product_id < 1775;

    Decode 函式的主要使用:

    求一箇中文排序方法,謝謝!

    SELECT * FROM TEMP ORDER BY 傳送狀態;

    傳送狀態  東城區  西城區  崇文區  宣武區  朝陽區  豐臺區  石景山區  海淀區  合計
    成功         1,473    1,113     297      103       875       1,037         7            576     5,481
    傳送中      144          3          14          0          30            0            11              8         210
    合計         1,619    1,116     324      103      1,125     1,224       47            584    6,142
    失敗             2           0           13          0          220        187         29              0          451

    想按照如下順序排序,指定按照“傳送狀態”欄位“成功”-“傳送中”,“失敗”,"合計"的順序,即:

    傳送狀態  東城區  西城區  崇文區  宣武區  朝陽區  豐臺區  石景山區  海淀區  合計
    成功         1,473    1,113     297      103       875       1,037         7            576     5,481
    傳送中      144          3          14          0          30            0            11              8         210
    失敗             2           0           13          0          220        187         29              0          451
    合計         1,619    1,116     324      103      1,125     1,224       47            584     6,142

    SELECT * FROM TEMP ORDER BY DECODE(傳送狀態,'成功',1,'傳送中',2,....)

    來源於select name,id from tmp order by 2;按列位置排序,使用decode則可以動態指定

    主要使用在查詢結果的行列轉換上,如:
  
    1、 固定列數的行列轉換

    如

    name subject grade
    ---------------------------
    student1 語文 80
    student1 數學 70
    student1 英語 60
    student2 語文 90
    student2 數學 80
    student2 英語 100
    ……

    轉換為

    語文 數學 英語
    student1 80 70 60
    student2 90 80 100

    按照name分組得到的結果,所以decode外面要套個sum,不然group by出現語義錯誤,結果為

    select name,sum(decode(subject,'語文', grade,null)) "語文",
    sum(decode(subject,'數學', grade,null)) "數學",
    sum(decode(subject,'英語', grade,null)) "英語"
    from student
    group by name

    2、 不定列行列轉換

   

    c1 c2
    --------------
    1 我
    1 是
    1 誰
    2 知
    2 道
    3 不
    ……

    轉換為

    1 我是誰
    2 知道
    3 不

    這一型別的轉換必須藉助於PL/SQL 來完成,這裡給一個例子

    CREATE OR REPLACE FUNCTION get_c2(tmp_c1 NUMBER)
    RETURN VARCHAR2
    IS
    Col_c2 VARCHAR2(4000);
    BEGIN
    FOR cur IN (SELECT c2 FROM t WHERE c1=tmp_c1) LOOP
    Col_c2 := Col_c2||cur.c2;
    END LOOP;
    Col_c2 := rtrim(Col_c2,1);
    RETURN Col_c2;
    END;
    /
    SQL> select distinct c1 ,get_c2(c1) cc2 from table;即可

    或者不用pl/sql,利用分析函式和 CONNECT_BY 實現:
    
    SELECT c1, SUBSTR (MAX (SYS_CONNECT_BY_PATH (c2, ';')), 2) NAME
    FROM (SELECT c1, c2, rn, LEAD (rn) OVER (PARTITION BY c1 ORDER BY rn) rn1
    FROM (SELECT c1, c2, ROW_NUMBER () OVER (ORDER BY c2) rn
    FROM t))
    START WITH rn1 IS NULL
    CONNECT BY rn1 = PRIOR rn
    GROUP BY c1;

    3、列數不固定(交叉錶行列轉置)

    這種是比較麻煩的一種,需要藉助pl/sql:

    原始資料:

    CLASS1 CALLDATE CALLCOUNT
    1 2005-08-08 40
    1 2005-08-07 6
    2 2005-08-08 77
    3 2005-08-09 33
    3 2005-08-08 9
    3 2005-08-07 21

    轉置後:

    CALLDATE CallCount1 CallCount2 CallCount3
    ------------ ---------- ---------- ----------
    2005-08-09 0 0 33
    2005-08-08 40 77 9
    2005-08-07 6 0 21

    試驗如下:

    1). 建立測試表和資料

    CREATE TABLE t(
    class1 VARCHAR2(2 BYTE),
    calldate DATE,
    callcount INTEGER
    );
    INSERT INTO t(class1, calldate, callcount)
    VALUES ('1', TO_DATE ('08/08/2005', 'MM/DD/YYYY'), 40);
    INSERT INTO t(class1, calldate, callcount)
    VALUES ('1', TO_DATE ('08/07/2005', 'MM/DD/YYYY'), 6);
    INSERT INTO t(class1, calldate, callcount)
    VALUES ('2', TO_DATE ('08/08/2005', 'MM/DD/YYYY'), 77);
    INSERT INTO t(class1, calldate, callcount)
    VALUES ('3', TO_DATE ('08/09/2005', 'MM/DD/YYYY'), 33);
    INSERT INTO t(class1, calldate, callcount)
    VALUES ('3', TO_DATE ('08/08/2005', 'MM/DD/YYYY'), 9);
    INSERT INTO t(class1, calldate, callcount)
    VALUES ('3', TO_DATE ('08/07/2005', 'MM/DD/YYYY'), 21);
    COMMIT ;

    2) 建立ref cursor準備輸出結果集

    CREATE OR REPLACE PACKAGE pkg_getrecord
    IS
    TYPE myrctype IS REF CURSOR;
    END pkg_getrecord;
    /

    3). 建立動態sql交叉表函式,輸出結果集

    CREATE OR REPLACE FUNCTION fn_rs
    RETURN pkg_getrecord.myrctype
    IS
    s VARCHAR2 (4000);
    CURSOR c1 IS
    SELECT ',sum(case when Class1='
    || class1
    || ' then CallCount else 0 end)'
    || ' "CallCount'
    || class1
    || '"' c2
    FROM t
    GROUP BY class1;
    r1 c1%ROWTYPE;
    list_cursor pkg_getrecord.myrctype;
    BEGIN
    s := 'select CallDate ';
    OPEN c1;
    LOOP
    FETCH c1 INTO r1;
    EXIT WHEN c1%NOTFOUND;
    s := s || r1.c2;
    END LOOP;
    CLOSE c1;
    s := s || ' from T group by CallDate order by CallDate desc ';
    OPEN list_cursor FOR s;
    RETURN list_cursor;
    END fn_rs;
    /

    4). 測試在sql plus下執行:

    var results refcursor;
    exec :results := fn_rs;
    print results;
    CALLDATE CallCount1 CallCount2 CallCount3
    --------------- ---------- ---------- ----------
    2005-08-09 0 0 33
    2005-08-08 40 77 9
    2005-08-07 6 0 21
   
    4、 不分組只簡單行列互換,並且每組之間用虛線分割,可以使用procedure,程式碼如下:

    create or replace
    procedure print_table(p_query in varchar2)
    authid current_user as
    l_thecursor integer default dbms_sql.open_cursor;
    l_columnvalue varchar2(4000);
    l_status integer;
    l_desctbl dbms_sql.desc_tab;
    l_colcnt number;
    begin
    dbms_sql.parse(l_thecursor,p_query,dbms_sql.native);
    dbms_sql.describe_columns(l_thecursor,l_colcnt,l_desctbl);
    for i in 1..l_colcnt loop
    dbms_sql.define_column(l_thecursor,i,l_columnvalue,4000);
    end loop;
    l_status := dbms_sql.execute(l_thecursor);
    while(dbms_sql.fetch_rows(l_thecursor)>0) loop
    for i in 1..l_colcnt loop
    dbms_sql.column_value(l_thecursor,i,l_columnvalue);
    dbms_output.put_line(rpad(l_desctbl(i).col_name,30)||l_c
    olumnvalue);
    end loop;
    dbms_output.put_line('----------------------------------------------
    -------');
    end loop;
    exception
    when others then
    dbms_sql.close_cursor(l_thecursor);
    raise;
    null;
    end;
    /

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/23590362/viewspace-660113/,如需轉載,請註明出處,否則將追究法律責任。

相關文章