当前位置: 代码迷 >> Oracle开发 >> oracle行转换列解决办法
  详细解决方案

oracle行转换列解决办法

热度:45   发布时间:2016-04-24 07:27:57.0
oracle行转换列
有如下表
JOBID JOBDATE BEGINTIME ENDTIME
  1 2012-05-05 08:30 17:30
  1 2012-05-06 09:00 17:00
  2 2012-05-05 08:30 17:30
 ... ... ... ...

如何转成
JOBID 2012-05-05起 2012-05-05止 2012-05-06起 2012-05-06止 ...
  1 08:30 17:30 09:00 17:00  
  2 09:00 17:00  

请提供动态的存储过程。。。

------解决方案--------------------
SQL code
转Oracle 行列转换1、固定列数的行列转换如student subject grade--------- ---------- --------student1 语文 80student1 数学 70student1 英语 60student2 语文 90student2 数学 80student2 英语 100……转换为语文 数学 英语student1 80 70 60student2 90 80 100……语句如下:select student, sum(decode(subject,'语文', grade,null)) "语文",sum(decode(subject,'数学', grade,null)) "数学",sum(decode(subject,'英语', grade,null)) "英语"from tablegroup by student; 2、不定列行列转换如c1 c2--- -----------1 我1 是1 谁2 知2 道3 不……转换为1 我是谁2 知道3 不这一类型的转换可以借助于PL/SQL来完成,这里给一个例子CREATE OR REPLACE FUNCTION get_c2(tmp_c1 NUMBER)RETURN VARCHAR2ISCol_c2 VARCHAR2(4000);BEGINFOR cur IN (SELECT c2 FROM t WHERE c1=tmp_c1) LOOPCol_c2 := Col_c2||cur.c2;END LOOP;Col_c2 := rtrim(Col_c2,1);RETURN Col_c2;END;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 NULLCONNECT BY rn1 = PRIOR rnGROUP BY c1;3、列数不固定(交叉表行列转置)这种是比较麻烦的一种,需要借助pl/sql:原始数据:CLASS1     CALLDATE         CALLCOUNT1          2005-08-08       401          2005-08-07       62          2005-08-08       773          2005-08-09       333          2005-08-08       93          2005-08-07       21转置后:CALLDATE     CallCount1 CallCount2 CallCount3------------ ---------- ---------- ----------2005-08-09   0          0          332005-08-08   40         77         92005-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_getrecordIS    TYPE myrctype IS REF CURSOR;END pkg_getrecord;/3). 建立动态sql交叉表函数,输出结果集 CREATE OR REPLACE FUNCTION fn_rs    RETURN pkg_getrecord.myrctypeIS    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          332005-08-08      40         77         92005-08-07      6          0          21典型实例讲解:假如有如下表,其中各个i值对应的行数是不定的SQL> select * from t;I A D———- ———- ——————-1 b 2008-03-27 10:55:421 a2008-03-27 10:55:461 d 2008-03-27 10:55:302 z 2008-03-27 10:55:552 t 2008-03-27 10:55:59要获得如下结果,注意字符串需要按照D列的时间排序:1 d,b,a2 z,t这是一个比较典型的行列转换,有好几种实现方法 1.自定义函数实现create or replace function my_concat(n number)return varchar2istype typ_cursor is ref cursor;v_cursor typ_cursor;v_temp varchar2(10);v_result varchar2(4000):= ”;v_sql varchar2(200);beginv_sql := ‘select a from t where i=’ || n ||’ order by d’;open v_cursor for v_sql;loopfetch v_cursor into v_temp;exit when v_cursor%notfound;v_result := v_result ||’,’ || v_temp;end loop;return substr(v_result,2);end;SQL> select i,my_concat(i) from t group by i;I MY_CONCAT(I)———- ——————–1 d,b,a2 z,t虽然这种方式可以实现需求,但是如果表t的数据量很大,i的值又很多的情况下,因为针对每个i值都要执行一句select,扫描和排序的次数和i的值成正比,性能会非常差。2.使用sys_connect_by_pathselect i,ltrim(max(sys_connect_by_path(a,’,')),’,') afrom(select i,a,d,min(d) over(partition by i) d_min,(row_number() over(order by i,d))+(dense_rank() over (order by i)) numidfrom t)start with d=d_min connect by numid-1=prior numidgroup by i;从执行计划上来看,这种方式只需要扫描两次表,比自定义函数的方法,效率要高很多,尤其是表中数据量较大的时候。3.使用wm_sys.wm_concat这个函数也可以实现类似的行列转换需求,但是似乎没有办法做到直接根据另外一列排序,所以需要先通过子查询或者临时表排好序SQL> select i,wmsys.wm_concat(a) from t group by i;I WMSYS.WM_CONCAT(A)———- ——————–1 b,a,d2 z,tSQL> select i,wmsys.wm_concat(a)2 from3 (select * from t order by i,d)4 group by i;I WMSYS.WM_CONCAT(A)———- ——————–1 d,b,a2 z,t执行计划上看,只需要做一次表扫描就可以了,但是这个函数是加密过的,执行计划并不能显示函数内部的操作。-----行列转换一数据格式一CARD_CODE          Q        BAL--------- ---------- ----------001                1         27001                2         10001                3         36001                4         97002                1         96002                2         12002                3         15002                4         32数据格式二CARD_CODE         Q1         Q2         Q3         Q4--------- ---------- ---------- ---------- ----------001               27         10         36         97002               96         12         15         32--格式一到格式二SELECT a.card_code, SUM(decode(a.q, 1, a.bal, 0)) q1, SUM(decode(a.q, 2, a.bal, 0)) q2,    SUM(decode(a.q, 3, a.bal, 0)) q3, SUM(decode(a.q, 4, a.bal, 0)) q4 FROM my_card a GROUP BY a.card_code ORDER BY 1;  --格式二到格式一SELECT t.card_code, t.rn q, decode(t.rn, 1, t.q1, 2, t.q2, 3, t.q3, 4, t.q4) bal FROM (SELECT a.*, b.rn      FROM my_card_two a,        (SELECT rownum rn          FROM dual         CONNECT BY rownum <= 4) b) t ORDER BY 1, 2;  ------行列转换二数据格式一CARD_CODE Q--------- ------------------------------------------------001       quarter_1001       quarter_2001       quarter_3001       quarter_4002       quarter_1002       quarter_2002       quarter_3002       quarter_4数据格式二CARD_CODE Q--------- -----------------------------002       quarter_1;quarter_2;quarter_3;quarter_4001       quarter_1;quarter_2;quarter_3;quarter_4--格式一到格式二SELECT t1.card_code, substr(MAX(sys_connect_by_path(t1.q, ';')), 2) q  FROM (SELECT a.card_code, a.q,                row_number() over(PARTITION BY a.card_code ORDER BY a.q) rn           FROM my_card_t3 a) t1 START WITH t1.rn = 1CONNECT BY t1.card_code = PRIOR t1.card_code           AND t1.rn - 1 = PRIOR t1.rn GROUP BY t1.card_code; --格式二到格式一SELECT t.card_code,    substr(t.q,        instr(';' || t.q, ';', 1, rn),        instr(t.q || ';', ';', 1, rn) - instr(';' || t.q, ';', 1, rn)) q FROM (SELECT a.card_code, a.q, b.rn      FROM my_card_t4 a,        (SELECT rownum rn          FROM dual         CONNECT BY rownum <= 100) b     WHERE instr(';' || a.q, ';', 1, rn) > 0) t ORDER BY 1, 2; 相关阅读:跟着小布老师学oracle基础——学习笔记1 (wjy1208, 2011-5-18)使用Wrap加密,保护Oracle程序源代码 (realkid4, 2011-5-18)Oracle Partition详解 (feiyu84, 2011-5-18)索引优化执行路径两例 (realkid4, 2011-5-19)ORA-01591错误与分布式事务 (realkid4, 2011-5-20)Oracle中Kill session的研究 (chaobaojun, 2011-5-21)Oracle Clusterware (尛样儿, 2011-5-21)说说函数索引 (realkid4, 2011-5-22)Oracle并行操作——从串行到并行 (realkid4, 2011-5-24)
  相关解决方案