1. 程式人生 > >sys_connect_by_path 用法

sys_connect_by_path 用法

個人覺得這個函式其實很好用,sys_connect_by_path(欄位名, 2個欄位之間的連線符號),注意這裡的連線符號不要使用逗號,oracle會報錯,如果一定要用,可以使用replace替換一下,方法如下 REPLACE(欄位名,原字元,',')。
還有,這個函式使用之前必須先建立一個樹,否則無用。
舉個例子:
目的:將num值相等的專案寫成 seq1,seq2,seq3,……的形式
(SELECT num,REPLACE(MAX(sql0), ';', ',')
                         FROM (SELECT num, sys_connect_by_path(sql1, ';') AS sql0
                              FROM (SELECT num, sql1, rn, lead(rn) over(PARTITION BY num ORDER BY rn) rn1
                                       FROM (SELECT num, sql1, row_number() over(ORDER BY num, sql1 DESC) rn FROM tlsbk))
                              START WITH num = '1' AND rn1 IS NULL
                               CONNECT BY rn1 = PRIOR rn));
結果:
num REPLACE(MAX(sql0), ';', ',')
--------------------------------------------------------
1 sql0,sql1,sql2
2 sql20,sql21,sql23,sql24,sql25
3 sql30,sql31,sql32,sql33,sql34,sql35,sql36

樹結構和它的專用函式SYS_CONNECT_BY_PATH(網摘:http://blog.oracle.com.cn/html/83/t-122083.html)
簡單的樹型結構
關於樹的普通應用
學習了下這個函式, 用ORGINDUSTRIES的表做了個測試:
正常的樹型結構
select lpad(' ',6*(level-1))||industry,indlevel,indid,pindid
from ORGINDUSTRIES
start with indid=1
connect by pindid=prior indid
結果顯示如下
                Indlevel   indid pindid
       服裝與服飾             1          1          0
             服裝             2          2             1
                   女裝        3          3             2

倒型樹
下面這個例子是個”倒數”—倒過來的樹型結構
select lpad(' ',6*(level-1))||industry,indlevel,indid,pindid
from ORGINDUSTRIES
start with indid=20
connect by indid=prior pindid;
這是標準結果:
                            Indlevel indid pindid
二手服裝                   3        20    2
   服裝                    2        2        1
         服裝與服飾        1        1        0
結論
無論正樹還是倒樹, 關鍵就在於connect by的條件.
正樹:   必須是   ‘父’= prior ‘子’
倒樹:   必須是   ‘子’= prior ‘父’

樹型結構的條件過濾
採用樹型結構的話, 如果我們想將樹上的一個分支砍掉.   將分支後面的結構都拋棄掉, 這個可以實現麼?當然可以。 但是不是用where, where條件只能去除單一的條件。
所以, 這種樹型的過濾條件就需要加在connect by上面。

測試如下:由於用真實環境比較貼近實際,所以提前用下SYS_CONNECT_BY_PATH函式來顯示下環境

不加任何條件的環境:
select areaname,sys_connect_by_path(areaname,',')
from areas bb
start with areaname='中國大陸'
connect by parentareaid=prior areaid 

結果:
1        中國大陸,中國大陸
2        北京        ,中國大陸,北京
3        北京        ,中國大陸,北京,北京
4        東城區        ,中國大陸,北京,東城區
5        西城區        ,中國大陸,北京,西城區
22        廣東        ,中國大陸,廣東
23        廣州        ,中國大陸,廣東,廣州
24        汕尾        ,中國大陸,廣東,汕尾
25        潮陽        ,中國大陸,廣東,潮陽
46        上海        ,中國大陸,上海
47        上海        ,中國大陸,上海,上海
48        黃浦區        ,中國大陸,上海,黃浦區
49        閘北區        ,中國大陸,上海,閘北區


加了where過濾條件的SQL:
select areaname,sys_connect_by_path(areaname,',')
from areas bb
where bb.areaid>861000
start with areaname='中國大陸'
connect by parentareaid=prior areaid

結果為:
2        北京        ,中國大陸,北京
3        北京        ,中國大陸,北京,北京
4        東城區        ,中國大陸,北京,東城區
5        西城區        ,中國大陸,北京,西城區
22        廣東        ,中國大陸,廣東
23        廣州        ,中國大陸,廣東,廣州
24        汕尾        ,中國大陸,廣東,汕尾
25        潮陽        ,中國大陸,廣東,潮陽
46        上海        ,中國大陸,上海
47        上海        ,中國大陸,上海,上海
48        黃浦區        ,中國大陸,上海,黃浦區
49        閘北區        ,中國大陸,上海,閘北區

結論:去掉了“1        中國大陸,中國大陸”資料

加了connect by的過濾條件:
select areaname,sys_connect_by_path(areaname,',')
from areas bb
where bb.areaid>861000
start with areaname='中國大陸'
connect by parentareaid=prior areaid   and areaname<>'廣東'

結果為:
2        北京        ,中國大陸,北京
3        北京        ,中國大陸,北京,北京
4        東城區        ,中國大陸,北京,東城區
5        西城區        ,中國大陸,北京,西城區
46        上海        ,中國大陸,上海
47        上海        ,中國大陸,上海,上海
48        黃浦區        ,中國大陸,上海,黃浦區
49        閘北區        ,中國大陸,上海,閘北區

結論:去掉了整個廣東的分支,   在結果集中只有北京和上海


SYS_CONNECT_BY_PATH函式
採用SYS_CONNECT_BY_PATH函式為:

select industry,sys_connect_by_path(industry,'/')
from ORGINDUSTRIES
start with indid=3
connect by indid=prior pindid;

結果為:
女裝             /女裝
服裝             /女裝/服裝
服裝與服飾          /女裝/服裝/服裝與服飾

這樣的話, 就可以實現, 樹結構的結果集的單行拼接:

我們只需要取最大的欄位就OK了

測試如下:

select max(sys_connect_by_path(industry,'/'))
from ORGINDUSTRIES
start with indid=3
connect by indid=prior pindid;

結果為:
/女裝/服裝/服裝與服飾


複雜的樹型結構――多列變單列
樹型結構也分單樹和多樹(我的稱呼,實際上就是指單支和多支)
對於下面的這種情況, 我們必須要構造的樹就屬於單支樹。
原始環境
環境如下:
select * from test;

結果為:
1        n1
1        n2
1        n3
1        n4
1        n5
3        t1
3        t2
3        t3
3        t4
3        t5
3        t6
2        m1

造樹
指令碼如下:
select no,q,
   no+row_number() over( order by no) rn,
   row_number() over(partition by no order by no) rn1
from test

結果如下:
No   Q   RN RN1
1        n1        2        1
1        n2        3        2
1        n3        4        3
1        n4        5        4
1        n5        6        5
2        m1        8        1
3        t1        10        1
3        t2        11        2
3        t3        12        3
3        t4        13        4
3        t5        14        5
3        t6        15        6

每列的目的是:
RN1列主要的目的是分組, 按照value值‘1’,我們可以start with使用它。

RN列主要用來做connect by使用。 實際上它就是我們要的樹。
第一個支: 2,3,4,5,6
第二個支: 8
第三個支: 10,11,12,13,14,15

中間為什麼要斷掉:7,9   目的就是為了區別每個分支。 到後面看具體的SQL,就明白這裡的說法了。

殺手鐗
既然我們有了樹, 就可以使用樹型函式SYS_CONNECT_BY_PATH和connect by啦,來拼接我們所需要的多列值。

指令碼如下:
select no,sys_connect_by_path(q,',')
from (
select no,q,
   no+row_number() over( order by no) rn,
   row_number() over(partition by no order by no) rn1
from test
)
start with rn1=1
connect by rn-1=prior rn

結果為:
1        ,n1
1        ,n1,n2
1        ,n1,n2,n3
1        ,n1,n2,n3,n4
1        ,n1,n2,n3,n4,n5
2        ,m1
3        ,t1
3        ,t1,t2
3        ,t1,t2,t3
3        ,t1,t2,t3,t4
3        ,t1,t2,t3,t4,t5
3        ,t1,t2,t3,t4,t5,t6

終極武器
最終我們要的值,是單列值, 其實想想, 也就是最長的一行咯。 那麼就好辦了。 我們直接GROUP BY ,然後取MAX值。
指令碼如下:
select no,max(sys_connect_by_path(q,','))
from (
select no,q,
   no+row_number() over( order by no) rn,
   row_number() over(partition by no order by no) rn1
from test
)
start with rn1=1
connect by rn-1=prior rn
group by no

結果為:
1        ,n1,n2,n3,n4,n5
2        ,m1
3        ,t1,t2,t3,t4,t5,t6

如果覺得前面的‘,’不好看,可以使用ltrim去掉。 或者用substr也可以。
如下:
ltrim(max(sys_connect_by_path(q,',')),',')
或者
substr(max(sys_connect_by_path(q,',')),2)