假如有如下表,其中各個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??
執行計劃上看,只需要做一次表掃描就可以了,但是這個函數是加密過的,執行計劃并不能顯示函數內部的操作。
?