欢迎来到我的地盘:今天是

若得山花插满头,莫问奴归处!

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

假如有如下表,其中各个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;

从执行计划上来看,这种方式只需要扫描两次表,比自定义函数的方法,效率要高很多,尤其是表中数据量较大的时候:
eplan1

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
posted on 2009-08-24 17:00  莫问奴归处  阅读(697)  评论(0编辑  收藏  举报
轩轩娃