您的位置:首页 > 数据库 > Oracle

Oracle一个典型行列转换的几种实现方法

2009-08-24 17:00 691 查看
假如有如下表,其中各个i值对应的行数是不定的

SQL> select * from t;

I A D
---------- ---------- -------------------
1 b 2008-03-27 10:55:42
1 a 2008-03-27 10:55:46
1 d 2008-03-27 10:55:30
2 z 2008-03-27 10:55:55
2 t 2008-03-27 10:55:59
要获得如下结果,注意字符串需要按照D列的时间排序:

1 d,b,a
2 z,t
这是一个比较典型的行列转换,有好几种实现方法

1.自定义函数实现

create or replace function my_concat(n number)
return varchar2
is
type typ_cursor is ref cursor;
v_cursor typ_cursor;
v_temp varchar2(10);
v_result varchar2(4000):= '';
v_sql varchar2(200);
begin
v_sql := 'select a from t where i=' || n ||' order by d';
open v_cursor for v_sql;
loop
fetch 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,a
2 z,t
虽然这种方式可以实现需求,但是如果表t的数据量很大,i的值又很多的情况下,因为针对每个i值都要执行一句select,扫描和排序的次数和i的值成正比,性能会非常差。

2.使用sys_connect_by_path

select i,ltrim(max(sys_connect_by_path(a,',')),',') a
from
(
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)) numid
from t
)
start with d=d_min connect by numid-1=prior numid
group 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,d
2 z,t

SQL> select i,wmsys.wm_concat(a)
2 from
3 (select * from t order by i,d)
4 group by i;

I WMSYS.WM_CONCAT(A)
---------- --------------------
1 d,b,a
2 z,t
执行计划上看,只需要做一次表扫描就可以了,但是这个函数是加密过的,执行计划并不能显示函数内部的操作。



不知道大家还有没有更加高效的实现方式,欢迎指教^_^

http://www.ningoo.net/html/2008/how_to_do_string_aggregate_on_oracle.html

Introduction of WMSYS

WMSSYS is used to store all the metadata information for Oracle Workspace Manager. This user was introduced in Oracle9i and (like most Oracle9i supporting accounts) is locked by default. The user account is locked because we want the password to be public but restrict access to the account to the SYS schema. So, to unlock the account, DBA privileges are required.

This post will show you on how to use the method WMSSYS.WM_CONCAT to convert the row and columns in data table.

SQL> select version from v$instance;

VERSION
-----------------
10.2.0.1.0

SQL>
SQL> create table IDTABLE
2 (
3 id number,
4 val varchar2(20)
5 )
6 ;

Table created

SQL>
SQL> insert into IDTABLE (ID, VAL)
2 values (10, 'abc');

1 row inserted
SQL> insert into IDTABLE (ID, VAL)
2 values (10, 'abc');

1 row inserted
SQL> insert into IDTABLE (ID, VAL)
2 values (10, 'def');

1 row inserted
SQL> insert into IDTABLE (ID, VAL)
2 values (10, 'def');

1 row inserted
SQL> insert into IDTABLE (ID, VAL)
2 values (20, 'ghi');

1 row inserted
SQL> insert into IDTABLE (ID, VAL)
2 values (20, 'jkl');

1 row inserted
SQL> insert into IDTABLE (ID, VAL)
2 values (20, 'mno');

1 row inserted
SQL> insert into IDTABLE (ID, VAL)
2 values (20, 'mno');

1 row inserted

SQL> select id,val from idtable;

ID VAL
---------- --------------------
10 abc
10 abc
10 def
10 def
20 ghi
20 jkl
20 mno
20 mno

8 rows selected

SQL> commit;

Commit complete

SQL>
SQL> SELECT ID, WMSYS.WM_CONCAT(VAL) AS ENAMES
2 FROM IDTABLE
3 GROUP BY ID;

ID ENAMES
---------- --------------------------------------------------------------------------------
10 abc,abc,def,def
20 ghi,jkl,mno,mno

SQL>
SQL> SELECT ID, WMSYS.WM_CONCAT(DISTINCT VAL) AS ENAMES
2 FROM IDTABLE
3 GROUP BY ID
4 ORDER BY ID;

ID ENAMES
---------- --------------------------------------------------------------------------------
10 abc,def
20 ghi,jkl,mno

SQL>
SQL> SELECT ID, VAL, WMSYS.WM_CONCAT(VAL) OVER(PARTITION BY ID) AS ENAMES
2 FROM IDTABLE
3 ORDER BY ID;

ID VAL ENAMES
---------- -------------------- --------------------------------------------------------------------------------
10 abc abc,abc,def,def
10 abc abc,abc,def,def
10 def abc,abc,def,def
10 def abc,abc,def,def
20 ghi ghi,jkl,mno,mno
20 jkl ghi,jkl,mno,mno
20 mno ghi,jkl,mno,mno
20 mno ghi,jkl,mno,mno

8 rows selected

SQL>
SQL> SELECT ID, VAL, WMSYS.WM_CONCAT(VAL) OVER(ORDER BY ID, VAL) AS ENAMES
2 FROM IDTABLE
3 ORDER BY ID;

ID VAL ENAMES
---------- -------------------- --------------------------------------------------------------------------------
10 abc abc,abc
10 abc abc,abc
10 def abc,abc,def,def
10 def abc,abc,def,def
20 ghi abc,abc,def,def,ghi
20 jkl abc,abc,def,def,ghi,jkl
20 mno abc,abc,def,def,ghi,jkl,mno,mno
20 mno abc,abc,def,def,ghi,jkl,mno,mno

8 rows selected

http://www.cnblogs.com/mikemao/archive/2009/06/11/1501116.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: