Oracle中,用一條Sql實現任意的行轉列拼接(不是decode)
說明一下測試環境:Oracle9i,有朋友說10g上測試結果不正確,本人沒有條件,所以無法進行測試
表結構和資料如下(表名Test):
NO VALUE NAME
1 a 測試1
1 b 測試2
1 c 測試3
1 d 測試4
2 e 測試5
4 f 測試6
4 g 測試7
Sql語句:
select No,
ltrim(max(sys_connect_by_path(Value, ';')), ';') as Value,
ltrim(max(sys_connect_by_path(Name, ';')), ';') as Name
from (select No,
Value,
Name,
rnFirst,
lead(rnFirst) over(partition by No order by rnFirst) rnNext
from (select a.No,
a.Value,
a.Name,
row_number() over(order by a.No, a.Value desc) rnFirst
from Test a) tmpTable1) tmpTable2
start with rnNext is null
connect by rnNext = prior rnFirst
group by No;
檢索結果如下:
NO VALUE NAME
1 a;b;c;d 測試1;測試2;測試3;測試4
2 e 測試5
4 f;g 測試6;測試7
簡單解釋一下那個Sql吧:
1、最內層的Sql(即表tmpTable1),按No和Value排序,並列出行號:
select a.No,
a.Value,
a.Name,
row_number() over(order by a.No, a.Value desc) rnFirst
from Test a
該語句結果如下:
NO VALUE NAME RNFIRST
1 d 測試4 1
1 c 測試3 2
1 b 測試2 3
1 a 測試1 4
2 e 測試5 5
4 g 測試7 6
4 f 測試6 7
2、外層的Sql(即表tmpTable2),根據No分割槽,取出當前行對應的下一條記錄的行號欄位:
select No,
Value,
Name,
rnFirst,
lead(rnFirst) over(partition by No order by rnFirst) rnNext
from (這裡是tmpTable1的SQL) tmpTable1
lead(rnFirst):取得下一行記錄的rnFirst欄位
over(partition by No order by rnFirst) 按rnFirst排序,並按No分割槽,分割槽就是如果下一行的No欄位與當前行的No欄位不相等時,不取下一行記錄顯示
該語句結果如下:
NO VALUE NAME RNFIRST RNNEXT
1 d 測試4 1 2
1 c 測試3 2 3
1 b 測試2 3 4
1 a 測試1 4 NULL
2 e 測試5 5 NULL
4 g 測試7 6 7
4 f 測試6 7 NULL
3、最後就是最外層的sys_connect_by_path函式與start遞迴了
sys_connect_by_path(Value, ';')
start with rnNext is null
connect by rnNext = prior rnFirst
這個大概意思就是從rnNext為null的那條記錄開始,遞迴查詢,
如果前一記錄的rnFirst欄位等於當前記錄的rnNext欄位,就把2條記錄的Value用分號連線起來,
大家可以先試試下面這個沒有Max和Group的Sql:
select No,
sys_connect_by_path(Value, ';') as Value,
sys_connect_by_path(Name, ';') as Name
from (select No,
Value,
Name,
rnFirst,
lead(rnFirst) over(partition by No order by rnFirst) rnNext
from (select a.No,
a.Value,
a.Name,
row_number() over(order by a.No, a.Value desc) rnFirst
from Test a) tmpTable1) tmpTable2
start with rnNext is null
connect by rnNext = prior rnFirst
結果是:
NO VALUE NAME
1 ;a ;測試1
1 ;a;b ;測試1;測試2
1 ;a;b;c ;測試1;測試2;測試3
1 ;a;b;c;d ;測試1;測試2;測試3;測試4
2 ;e ;測試5
4 ;f ;測試6
4 ;f;g ;測試6;測試7
可以看到,每個No的最後一條記錄就是我們要的了
所以在sys_connect_by_path外面套一個Max,再加個Group by No,得到的結果就是行轉列的結果了
最後再加一個Ltrim,去掉最前面的那個分號,完成。
你看明白了嗎?