樹結構和它的專用函數SYS_CONNECT_BY_PATH

          Posted on 2009-09-20 21:32 林光炎 閱讀(1233) 評論(0)  編輯  收藏 所屬分類: ORACLE
          簡單的樹型結構
          關于樹的普通應用
          學習了下這個函數, 用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)

          posts - 104, comments - 33, trackbacks - 0, articles - 0

          Copyright © 林光炎

          主站蜘蛛池模板: 阿拉善右旗| 赤城县| 桃江县| 元氏县| 马公市| 平昌县| 安徽省| 永川市| 白朗县| 海门市| 嘉善县| 九江市| 梅州市| 柯坪县| 军事| 克东县| 柳江县| 云阳县| 金塔县| 玉树县| 邛崃市| 沁阳市| 淮滨县| 霸州市| 保定市| 叙永县| 遂昌县| 二手房| 焦作市| 巴林右旗| 南汇区| 千阳县| 随州市| 油尖旺区| 班玛县| 朝阳区| 广灵县| 莱西市| 溆浦县| 莎车县| 游戏|