有如下表
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)