1. 程式人生 > >oracle:兩條sql語句實現層次查詢的反序權值計算與輸出

oracle:兩條sql語句實現層次查詢的反序權值計算與輸出

      這張存放商品及零件資訊(事實上行業裡有專門的名字,這裡由於種種原因暫且使用這樣一種不太確切的名字)的表內有三個主要的欄位:父節點程式碼,子節點程式碼,權重。父節點與子節點用來描述商品或所含零件之間的上下級包含關係,最終的成品程式碼一定是最上層的根節點。而權重描述了每個子節點(零件)與其父節點之間的數量對應關係。比如,一臺完整的電腦就是最終的商品,它由顯示器,主機,滑鼠,鍵盤構成,而主機又由主機板,cpu,硬碟,記憶體,光碟機等零件構成,主機板又分為......可以用一幅圖直觀反映出來

                                                               圖1 商品與零件構成樹

 那麼,一臺計算機對應一個主機或者說他們的構成比例是1:1,這也就是主機子節點的權重。而一臺主機如果包含兩塊硬碟的話,則硬碟的權重就是1:2。如果兩臺計算機公用一臺顯示器,則顯示器的權重就是2:1。對於這樣的表結構,客戶的要求就是按照從上到下從右到左的順序輸出每個節點以及其權重,需要注意的是此時的權重並不單純是該子節點本身在表中儲存的權重值,而是以根節點個數1為基準,向子節點依次累計的權重值,這裡稱為累計因數。比如如果一塊硬碟有四張碟片,則對於一臺電腦而言包含1*2*4=8張碟片,碟片的累計因數是8。

         此時,要想輸出樹狀層次的資料我們會首選connect by...start with...語句,如下:

 SELECT
    level,
    pare_item_cod,
    chil_item_cod
FROM
    m_structure
WHERE
    del_F=0   start with del_F=0 AND
    pare_item_Cod='MN2_TEST1-1'  connect by prior chil_item_cod=pare_item_cod

其中'MN2_TEST1-1'  是根節點程式碼也是父節點程式碼,這條語句執行時將從該節點往下從左邊子節點一直找到最左邊葉子結點然後再找其父節點的右子節點,對整棵樹進行深度優先搜尋。這一過程是遞迴進行的。但是,它並不滿足我們對順序的要求。同時,每個節點的累計因數也算不出來,因為我們無法控制遞迴的過程,以在此過程中計算累計因數。如圖2所示:

                                                             圖2 普通層次查詢語句的輸出結果

為了解決遍歷順序的問題,我們首先會想到增加order by字句,即先按照父節點pare_item_cod按升序排列,然後對子結點chil_item_cod按降序排列,那麼遞迴的過程就是從上到下,從右到左。查詢語句如下:

SELECT
    level,
    pare_item_cod,
    chil_item_cod
FROM
    m_structure
WHERE
    del_F=0   start with del_F=0 AND
    pare_item_Cod='MN2_TEST1-1'  connect by prior chil_item_cod=pare_item_cod
ORDER BY
    pare_item_cod asc,
    chil_item_cod desc

執行結果怎樣呢,如下圖3所示,事情並沒有想象中那麼簡單。

                                 圖3 使用order by子句的遞迴順序

顯然,單單使用order by 子句會破壞遞迴結果的層次關係,使整個sql語句喪失了原有的意義。order siblings by關鍵字恰恰能解決這樣的問題,它既能維持遞迴時的層次關係,又能使得同層節點按照規定的順序輸出。執行結果如圖4所示。

                                   圖4 使用order siblings by使得遍歷結果按照規定的順序輸出

    至此,節點輸出順序的問題已經得到解決,下面來看如何計算各層節點的累計因子。儘管start with ...connect by這樣的遞迴查詢是一次性完成的,我們不能控制其執行的過程,從而在遍歷的每個階段將各節點的累積因子統計出來,但是我們會發現一個規律,就是各節點的累積因子就是從該結點按照遞迴順序的逆序回到根節點所碰到的所有節點累積因子的乘積。這樣一來,只要我們能夠知道一個節點到最上層根節點的搜尋過程中都有哪些節點,問題就有希望得到解決。這時,我們還是要利用oracle層次查詢語句中的一個特徵:遍歷路徑SYS_CONNECT_BY_PATH(chil_item_cod,‘/’) AS PATH,它會將一個節點到根節點間的所有搜尋到的節點輸出成一個字串,中間用“/”分隔。接下來我們需要模仿上面的sql語句再寫一條sql語句,不同的是這條層次便利的sql語句是從某一子節點開始一直遍歷到根節點的,如下:

SELECT
    level,
    chil_item_cod,
    SYS_CONNECT_BY_PATH(chil_item_cod,‘/’) AS PATH
FROM
    m_structure
WHERE
    del_F=0   start with del_F=0 AND
    pare_item_Cod='MN2_TESTXX'  connect by prior pare_item_cod=chil_item_cod

其中,start with條件中的pare_item_cod節點名來自於上面一條sql語句輸出節點中的一個。比如上面一條sql語句輸出樹最右邊的“4-2”節點,那麼這裡pare_item_Cod='MN2_TEST4-2'。輸出的PATH資料域的值就是 “2-1/3-2/3-1/4-2”,當然最上面的根我們是知道的“1-1” 。這樣就得到了從根節點到指定子節點的所有遍歷節點序列,下面的事情就是在程式中使用一個迴圈將這裡各個節點的權值從上到下乘起來,就能夠得到指定的那個子節點的累計因子。

     總結一下,通過圖4中遞迴查詢語句與上面這條遞迴查詢語句相結合,我們不但可以對層次型的資料按照指定的順序進行輸出,還可以完成各個節點上的累計因子的計算。其間,我們不但使用了oracle的基本層次查詢關鍵字start with...connect by,還綜合使用了order siblings by關鍵字以及遍歷路徑輸出關鍵字sys_connect_by_path(),再配合具體的程式邏輯,實現比較複雜的功能。當然,這篇文章論述的原理和特性大多基於oracle,如果使用sql server或者db2資料庫則需要另闢蹊徑。