摘要:其他的樹形結(jié)構(gòu)數(shù)據(jù)像職員與經(jīng)理的關(guān)系,菜單等等很多方案以下所有方案中暫不考慮外鍵約束,數(shù)據(jù)庫是鄰接表這個可能是最常見的解決方案,直接添加字段,引用同一張表中的其他回復(fù)。
個人博客:http://www.80soho.com/?p=781場景:
有這么個需求:設(shè)計開發(fā)一個評論系統(tǒng),要求用戶可以評論文章以及相互回復(fù),無層級數(shù)限制。
這個需求開發(fā)人員基本都遇到過,可以先回憶或考慮這個數(shù)據(jù)表如何設(shè)計!
存在遞歸關(guān)系的數(shù)據(jù)很常見,數(shù)據(jù)常會像樹或者以層級方式組織。在樹形結(jié)構(gòu)中,實例被稱為節(jié)點(node),每個節(jié)
點有多個子節(jié)點和一個父節(jié)點,最上面的節(jié)點叫根(root)節(jié)點,它沒有父節(jié)點,最底層的沒有子節(jié)點的節(jié)點叫葉
(leaf), 中間的節(jié)點簡單地稱為非葉(nonleaf)節(jié)點。
評論數(shù)據(jù)就是一種樹形結(jié)構(gòu)數(shù)據(jù),評論的子節(jié)點就是它的回復(fù)。其他的樹形結(jié)構(gòu)數(shù)據(jù)像職員與經(jīng)理的關(guān)系,菜單等等很多;
鄰接表
這個可能是最常見的解決方案,直接添加parent_id字段,引用同一張表中的其他回復(fù)。表結(jié)構(gòu)如下
CREATE TABLE `Comments` ( `comment_id` int(11) NOT NULL AUTO_INCREMENT COMMENT "評論ID", `parent_id` int(11) NOT NULL DEFAULT "0" COMMENT "評論的父ID", `article_id` int(11) NOT NULL DEFAULT "0" COMMENT "文章ID", `comment` varchar(200) DEFAULT "" COMMENT "評論內(nèi)容", PRIMARY KEY (`comment_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT="簡化的評論表";
鄰接表總是依賴父節(jié)點,看看它的優(yōu)缺點:
無法完成樹操作中最普通的有一項,查詢一個節(jié)點的所有后代;
要用一條簡單的sql檢索一個很長的回復(fù)分支還是很困難的;
也可先獲取文章的所有評論,在程序的棧內(nèi)存中處理整合,但數(shù)據(jù)量,訪問量都大,每次有人訪問都要做一次數(shù)據(jù)處理也不切實際;
增加葉子節(jié)點操作是非常方便的;
刪除節(jié)點會變得比較復(fù)雜:考慮數(shù)據(jù)完整性,刪除一棵子樹,不得不考慮處理其所有的后代節(jié)點;
上述這種方案可被叫做:‘單純的數(shù)‘ 反模式!要合理的使用反模式:鄰接表設(shè)計的優(yōu)勢在于能快速地獲取一個給定節(jié)點的直接父節(jié)點,也很容易插入新節(jié)點。如果這樣的需求就是你的應(yīng)用程序的需求,那使用鄰接表就可以很好地工作!
下面再看看其他的方案:
路徑枚舉
路徑枚舉的設(shè)計通過將所有的祖先的信息聯(lián)合成一個字符串,并保存為每個節(jié)點的一個屬性:
CREATE TABLE `Comments` ( `comment_id` int(11) NOT NULL AUTO_INCREMENT COMMENT "評論ID", `path` varchar(1000) NOT NULL DEFAULT "0" COMMENT "路徑:eg: 1/2/4", `article_id` int(11) NOT NULL DEFAULT "0" COMMENT "文章ID", `comment` varchar(200) DEFAULT "" COMMENT "評論內(nèi)容", PRIMARY KEY (`comment_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT="簡化的評論表";
來看看該方案有沒有解決鄰接表的問題,
1. 通過比較每個節(jié)點的路徑來查詢一個節(jié)點的祖先:例如查找comment_id 為4的所有的祖先的sql:
SELECT * from Comments AS c where "1/3/4/" like CONCAT(c.path,"%");
2. 查詢一個節(jié)點的所有后臺:例如 comment_id 為 1 的所有后臺:
SELECT * from Comments AS c where c.path like CONCAT("1/","%");
3. 插入節(jié)點:只需一份父節(jié)點的路徑即可;comment_id是自動生成,需要先插入再修改;
該方案的缺點:數(shù)據(jù)庫不能確保路徑的格式總是正確或者路徑中的節(jié)點確實存在,需應(yīng)用程序的邏輯代碼來維護,且驗證字符串的正確性的開銷很大;再者,無論將varcharde的長度設(shè)定為多大,依舊存在長度限制,因而不能支持樹結(jié)構(gòu)的無限擴展。
閉包表
閉包表記錄樹中所有節(jié)點間的關(guān)系,而不僅僅只有那些直接的父子關(guān)系,是一個簡單而優(yōu)雅的分級存儲解決方案。
該方案不再使用Comments表來存儲樹的結(jié)構(gòu),而是將樹中任何具有祖先-后代關(guān)系的節(jié)點對都存儲在新表中,即使兩個節(jié)點不是直接的父子關(guān)系,同時,還增加一行指向節(jié)點自己,表結(jié)構(gòu)如下:
CREATE TABLE `Comments` ( `comment_id` int(11) NOT NULL AUTO_INCREMENT COMMENT "評論ID", `article_id` int(11) NOT NULL DEFAULT "0" COMMENT "文章ID", `comment` varchar(200) DEFAULT "" COMMENT "評論內(nèi)容", PRIMARY KEY (`comment_id`) ) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8 COMMENT="簡化的評論表";
CREATE TABLE `TreePaths` ( `ancestor` int(11) NOT NULL DEFAULT "0" COMMENT "祖先", `descendant` int(11) NOT NULL DEFAULT "0" COMMENT "后代" ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
下面看看相關(guān)操作
1. 搜索祖先:搜索評論5的祖先:
SELECT c.* from Comments as c JOIN TreePaths as t on c.`comment_id` = t.ancestor WHERE t.descendant=5;
2. 搜索后臺:搜索評論1的后代:
SELECT c.* from Comments as c JOIN TreePaths as t on c.`comment_id` = t.descendant WHERE t.ancestor=1;
3. 插入子節(jié)點:
例如評論5新增一個子節(jié)點,應(yīng)首先插入一條自己到自己的關(guān)系,然后搜索TreePaths表中后代是評論5的所有節(jié)點,增加這些節(jié)點和新節(jié)點的’祖先-后代‘關(guān)系。
TreePaths表可以繼續(xù)優(yōu)化:增加path_length字段表示祖先與后代的層級數(shù)等等;
綜述
以上列舉了三個方案,沒種設(shè)計都各有優(yōu)劣,如何選擇設(shè)計依賴于應(yīng)用程序中的哪種操作最需要性能上的優(yōu)化:
鄰接表是最方面的設(shè)計,并且很多開發(fā)中都了解它;
路徑枚舉能夠很直觀地展示出祖先與后代之間的路徑,但同時由于它不能確保引用完整性,使得這個設(shè)計十分脆弱,
閉包表示通用的設(shè)計,它要求一個張額外的表來存儲關(guān)系,使用空間換時間的方案減少操作過程中冗余的計算所造成的消耗。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://m.hztianpu.com/yun/31140.html
摘要:樹和樹的算法一樹樹的概念樹英語是一種抽象數(shù)據(jù)類型或是實作這種抽象數(shù)據(jù)類型的數(shù)據(jù)結(jié)構(gòu),用來模擬具有樹狀結(jié)構(gòu)性質(zhì)的數(shù)據(jù)集合。一種時間復(fù)雜度額外空間復(fù)雜度的二叉樹的遍歷方式,為二叉樹的節(jié)點個數(shù)。 樹和樹的算法 一、樹 1.1 樹的概念 樹(英語:tree)是一種抽象數(shù)據(jù)類型(ADT)或是實作這種抽象數(shù)據(jù)類型的數(shù)據(jù)結(jié)構(gòu),用來模擬具有樹狀結(jié)構(gòu)性質(zhì)的數(shù)據(jù)集合。它是由n(n>=1)個有限節(jié)點組成一個...
摘要:樹和樹的算法一樹樹的概念樹英語是一種抽象數(shù)據(jù)類型或是實作這種抽象數(shù)據(jù)類型的數(shù)據(jù)結(jié)構(gòu),用來模擬具有樹狀結(jié)構(gòu)性質(zhì)的數(shù)據(jù)集合。一種時間復(fù)雜度額外空間復(fù)雜度的二叉樹的遍歷方式,為二叉樹的節(jié)點個數(shù)。 樹和樹的算法 一、樹 1.1 樹的概念 樹(英語:tree)是一種抽象數(shù)據(jù)類型(ADT)或是實作這種抽象數(shù)據(jù)類型的數(shù)據(jù)結(jié)構(gòu),用來模擬具有樹狀結(jié)構(gòu)性質(zhì)的數(shù)據(jù)集合。它是由n(n>=1)個有限節(jié)點組成一個...
摘要:題目要求將二叉搜索樹序列化和反序列化,序列化是指將樹用字符串的形式表示,反序列化是指將字符串形式的樹還原成原來的樣子。假如二叉搜索樹的節(jié)點較多,該算法將會占用大量的額外空間。 題目要求 Serialization is the process of converting a data structure or object into a sequence of bits so that...
閱讀 4559·2021-11-24 10:24
閱讀 1472·2021-11-22 15:22
閱讀 2160·2021-11-17 09:33
閱讀 2552·2021-09-22 15:29
閱讀 573·2019-08-30 15:55
閱讀 1718·2019-08-29 18:42
閱讀 2791·2019-08-29 12:55
閱讀 1836·2019-08-26 13:55