志當存高遠,功到自然成!

          少年強則中國強,少年進步則中國進步!

          BlogJava 首頁 新隨筆 聯系 聚合 管理
            53 Posts :: 2 Stories :: 2 Comments :: 0 Trackbacks
          一種理想的在關系數據庫中存儲樹型結構數據的方法
          2008-03-07 15:49
              在各種基于關系數據庫的應用系統開發中,我們往往需要存儲樹型結構的數據,目前有很多流行的方法,如鄰接列表模型(The Adjacency List Model),在此基礎上也有很多人針對不同的需求做了相應的改進,但總是在某些方面存在的各種各樣的缺陷。
              那么理想中的樹型結構應具備哪些特點呢?數據存儲冗余小、直觀性強;方便返回整個樹型結構數據;可以很輕松的返回某一子樹(方便分層加載);快整獲以某節 點的祖譜路徑;插入、刪除、移動節點效率高等等。帶著這些需求我查找了很多資料,發現了一種理想的樹型結構數據存儲及操作算法,改進的前序遍歷樹模型 (The Nested Set Model)。

          一、數據

              在本文中,舉一個在線食品店樹形圖的例子。這個食品店通過類別、顏色和品種來組織食品。樹形圖如下:

          二、鄰接列表模型(The Adjacency List Model)

          在這種模型下,上述數據在關系數據庫的表結構數據通常如下圖所示:

          由于該模型比較簡單,在此不再詳細介紹其算法,下面列出它的一些不足:

              在大多數編程語言中,他運行很慢,效率很差。這主要是“遞歸”造成的。我們每次查詢節點都要訪問數據庫。每次數據庫查詢都要花費一些時間,這讓函數處理龐 大的樹時會十分慢。造成這個函數不是太快的第二個原因可能是你使用的語言。不像Lisp這類語言,大多數語言不是針對遞歸函數設計的。對于每個節點造成這 個函數不是太快的第二個原因可能是你使用的語言。不像Lisp這類語言,大多數語言不是針對遞歸函數設計的。對于每個節點,函數都要調用他自己,產生新的 實例。這樣,對于一個4層的樹,你可能同時要運行4個函數副本。對于每個函數都要占用一塊內存并且需要一定的時間初始化,這樣處理大樹時遞歸就很慢了。

          三、改進的前序遍歷樹模型(The Nested Set Model)

          原理:

              我們先把樹按照水平方式擺開。從根節點開始(“Food”),然后他的左邊寫上1。然后按照樹的順序(從上到下)給“Fruit”的左邊寫上2。這樣,你 沿著樹的邊界走啊走(這就是“遍歷”),然后同時在每個節點的左邊和右邊寫上數字。最后,我們回到了根節點“Food”在右邊寫上18。下面是標上了數字 的樹,同時把遍歷的順序用箭頭標出來了。

              我們稱這些數字為左值和右值(如,“Food”的左值是1,右值是18)。正如你所見,這些數字按時了每個節點之間的關系。因為“Red”有3和6兩個 值,所以,它是有擁有1-18值的“Food”節點的后續。同樣的,我們可以推斷所有左值大于2并且右值小于11的節點,都是有2-11的“Fruit” 節點的后續。這樣,樹的結構就通過左值和右值儲存下來了。這種數遍整棵樹算節點的方法叫做“改進前序遍歷樹”算法。

          表結構設計:

          常用的操作:

          下面列出一些常用操作的SQL語句

          返回完整的樹(Retrieving a Full Tree)
          SELECT node.name
            
          FROM nested_category node, nested_category parent
          WHERE node.lft BETWEEN parent.lft AND parent.rgt
             
          AND parent.name = 'electronics'
          ORDER BY node.lft

          返回某結點的子樹(Find the Immediate Subordinates of a Node)
          SELECT V.*
            
          FROM (SELECT node.name,
                          (
          COUNT(parent.name) - (AVG(sub_tree.depth) + 1)) depth
                    
          FROM nested_category node,
                          nested_category parent,
                          nested_category sub_parent,
                          (
          SELECT V.*
                            
          FROM (SELECT node.name, (COUNT(parent.name) - 1) depth
                                    
          FROM nested_category node, nested_category parent
                                   
          WHERE node.lft BETWEEN parent.lft AND parent.rgt
                                     
          AND node.name = 'portable electronics'
                                   
          GROUP BY node.name) V,
                                  nested_category T
                           
          WHERE V.name = T.name
                           
          ORDER BY T.lft) sub_tree
                   
          WHERE node.lft BETWEEN parent.lft AND parent.rgt
                     
          AND node.lft BETWEEN sub_parent.lft AND sub_parent.rgt
                     
          AND sub_parent.name = sub_tree.name
                   
          GROUP BY node.name) V,
                  nested_category T
          WHERE V.name = T.name
             
          and V.depth <= 1
             
          and V.depth > 0
          ORDER BY T.Lft

          返回某結點的祖譜路徑(Retrieving a Single Path)
          SELECT parent.name
            
          FROM nested_category node, nested_category parent
          WHERE node.lft BETWEEN parent.lft AND parent.rgt
             
          AND node.name = 'flash'
          ORDER BY node.lft

          返回所有節點的深度(Finding the Depth of the Nodes)
          SELECT V.*
            
          FROM (SELECT node.name, (COUNT(parent.name) - 1) depth
                    
          FROM nested_category node, nested_category parent
                   
          WHERE node.lft BETWEEN parent.lft AND parent.rgt
                   
          GROUP BY node.name) V,
                  nested_category T
          WHERE V.name = T.name
          ORDER BY T.Lft

          返回子樹的深度(Depth of a Sub-Tree)
          SELECT V.*
            
          FROM (SELECT node.name,
                          (
          COUNT(parent.name) - (AVG(sub_tree.depth) + 1)) depth
                    
          FROM nested_category node,
                          nested_category parent,
                          nested_category sub_parent,
                          (
          SELECT V.*
                            
          FROM (SELECT node.name, (COUNT(parent.name) - 1) depth
                                    
          FROM nested_category node, nested_category parent
                                   
          WHERE node.lft BETWEEN parent.lft AND parent.rgt
                                     
          AND node.name = 'portable electronics'
                                   
          GROUP BY node.name) V,
                                  nested_category T
                           
          WHERE V.name = T.name
                           
          ORDER BY T.lft) sub_tree
                   
          WHERE node.lft BETWEEN parent.lft AND parent.rgt
                     
          AND node.lft BETWEEN sub_parent.lft AND sub_parent.rgt
                     
          AND sub_parent.name = sub_tree.name
                   
          GROUP BY node.name) V,
                  nested_category T
          WHERE V.name = T.name
          ORDER BY T.Lft

          返回所有的葉子節點(Finding all the Leaf Nodes)
          SELECT name FROM nested_category WHERE rgt = lft + 1

          插入節點(Adding New Nodes)
          LOCK TABLE nested_category WRITE;

          SELECT @myRight := rgt FROM nested_category WHERE name = 'TELEVISIONS';

          UPDATE nested_category SET rgt = rgt + 2 WHERE rgt > @myRight;
          UPDATE nested_category SET lft = lft + 2 WHERE lft > @myRight;

          INSERT INTO nested_category
             (name, lft, rgt)
          VALUES
             (
          'GAME CONSOLES', @myRight + 1, @myRight + 2);

          UNLOCK TABLES;

          刪除節點(Deleting Nodes)
          LOCK TABLE nested_category WRITE;

          SELECT @myLeft := lft, @myRight := rgt, @myWidth := rgt - lft + 1
            
          FROM nested_category
          WHERE name = 'GAME CONSOLES';

          DELETE FROM nested_category WHERE lft BETWEEN @myLeft AND @myRight;

          UPDATE nested_category SET rgt = rgt - @myWidth WHERE rgt > @myRight;
          UPDATE nested_category SET lft = lft - @myWidth WHERE lft > @myRight;

          UNLOCK TABLES;
          主站蜘蛛池模板: 托克托县| 永清县| 鄂托克前旗| 黑水县| 青海省| 滁州市| 新兴县| 尼勒克县| 南通市| 浙江省| 浠水县| 哈巴河县| 石景山区| 盖州市| 海城市| 滦平县| 图片| 岚皋县| 巴东县| 莱芜市| 凤台县| 山阴县| 兴仁县| 安福县| 嘉义市| 荆门市| 云林县| 芦溪县| 子洲县| 苍南县| 垦利县| 广安市| 屏边| 海城市| 钦州市| 兴仁县| 喀喇| 丰宁| 澜沧| 延长县| 永登县|