2010-02-01 69 views
33

我正在寫一個從Tree和TreeNode組合而成的數據樹結構。樹將包含數據的根級和頂級操作。 我正在使用一個用戶界面庫來呈現樹窗口窗體中我可以將樹綁定到TreeView。如何在SQL中表示數據樹?

我需要將這個樹和節點保存在數據庫中。 什麼將是最好的方式來保存樹和獲得以下功能:

  1. 直觀的實施。
  2. 簡單的綁定。將很容易從樹移動到DB結構和回(如果有)

我有2個想法。首先是將數據序列化成表格中的單行數據。 第二個是保存在表格中,但是當移動到數據實體時,我將丟失已更改節點上的表上的行狀態。

任何想法?

+0

您正在使用什麼數據庫? – 2010-02-01 10:17:18

+3

另請參見[什麼是最有效的/優雅的方式來解析一個扁平的表到樹?](http://stackoverflow.com/questions/192220/what-is-the-most-efficient-elegant-way-to -parse-a-flat-table-into-a-tree/192462#192462) – 2012-12-26 18:08:23

+2

另請參見[在關係數據庫中存儲分層數據的選項?](http://stackoverflow.com/questions/4048151/什麼是選擇存儲分層數據關係數據庫) – ghord 2015-04-11 12:20:48

回答

3

這取決於你將如何查詢和更新數據。如果您將所有數據存儲在一行中,則基本上只有一個單元,您無法重寫所有數據而無法查詢或部分更新。

如果你想將每個元素作爲一行存儲,你應該首先閱讀Managing Hierarchical Data in MySQL(特定於MySQL,但也適用於其他許多數據庫)。

如果您只訪問整個樹,則鄰接列表模型會很難在不使用遞歸查詢的情況下檢索根下的所有節點。如果添加一個連接到頭部的額外列,那麼您可以執行SELECT * WHERE head_id = @id並將整棵樹放在一個非遞歸查詢中,但它會使數據庫非標準化。

某些數據庫具有自定義擴展功能,可以更容易地存儲和檢索角色數據,例如Oracle擁有CONNECT BY

+0

+1嵌套集,我會發出同樣的文章 – Eineki 2010-02-01 10:26:34

28

最簡單的實現是鄰接表結構:

id parent_id data 

然而,一些數據庫,特別是MySQL,在處理這個模型的一些問題,因爲它需要運行哪些MySQL沒有遞歸查詢的能力。

另一種模式是嵌套集合

id lft rgt data 

其中lftrgt是定義層次任意值(任何孩子的lftrgt應在任何父母的lftrgt

這不需要遞歸查詢,但它更難以維護。

但是,在MySQL這可以改進使用SPATIAL abitilies。

請參閱以下文章在我的博客:

進行更詳細的解釋。

0

類似於表「節點」,其中每個節點行包含父節點id(除普通節點數據外)。對於根,父項是NULL。

當然,這使得查找孩子更費時,但這樣一來,實際的數據庫將會非常簡單。

16

我書籤這slidshare有關SQL-反模式,其中討論了幾個備選方案:http://www.slideshare.net/billkarwin/sql-antipatterns-strike-back?src=embed

從那裏是使用封閉表(它在幻燈片中解釋)的建議。

下面是總結(幻燈片77):

    | Query Child | Query Subtree | Modify Tree | Ref. Integrity 
Adjacency List | Easy  |  Hard  | Easy  |  Yes 
Path Enumeration | Easy  |  Easy  | Hard  |  No 
Nested Sets  | Hard  |  Easy  | Hard  |  No 
Closure Table  | Easy  |  Easy  | Easy  |  Yes 
+4

這是一個非常好的參考,從幻燈片48開始 - http://www.slideshare.net/billkarwin/sql-antipatterns-strike-back/48 – mahemoff 2014-07-17 14:35:53

0

最好的辦法,我覺得確實是給每個節點ID和PARENT_ID,其中父ID是父節點的ID。這有幾個好處

  1. 當你想更新一個節點時,你只需要重寫那個節點的數據。
  2. 當您只想查詢某個節點時,您可以準確獲得所需的信息,從而減少數據庫連接的開銷
  3. 許多編程語言都具有將mysql數據轉換爲XML或json的功能,將使使用api打開您的應用程序更容易。
6

我很驚訝,沒有人提到物化路徑解決方案,這可能是使用標準SQL中樹的最快方式。

在此方法中,樹中的每個節點都有一列路徑,其中存儲從根節點到節點的完整路徑。這涉及非常簡單和快速的查詢。

看一看示例表節點

+---------+-------+ 
| node_id | path | 
+---------+-------+ 
| 0  |  | 
| 1  | 1  | 
| 2  | 2  | 
| 3  | 3  | 
| 4  | 1.4 | 
| 5  | 2.5 | 
| 6  | 2.6 | 
| 7  | 2.6.7 | 
| 8  | 2.6.8 | 
| 9  | 2.6.9 | 
+---------+-------+ 

爲了得到節點的孩子X,您可以編寫以下查詢:

SELECT * FROM node WHERE path LIKE CONCAT((SELECT path FROM node WHERE node_id = x), '.%') 

請介意,那列路徑應該被索引,以便快速執行LIKE claus即

+2

Björn給出的早期鏈接http://www.slideshare.net/billkarwin/sql-antipatterns-strike-back?src =嵌入涵蓋了這一點,也解釋了爲什麼它更喜歡推薦Closure表,值得一讀。 – 2013-11-18 12:41:06

3

如果您使用的是PostgreSQL,您可以使用ltree,這是一個實現樹數據結構的contrib擴展中的包(默認情況下爲)。

docs

CREATE TABLE test (path ltree); 
INSERT INTO test VALUES ('Top'); 
INSERT INTO test VALUES ('Top.Science'); 
INSERT INTO test VALUES ('Top.Science.Astronomy'); 
INSERT INTO test VALUES ('Top.Science.Astronomy.Astrophysics'); 
INSERT INTO test VALUES ('Top.Science.Astronomy.Cosmology'); 
INSERT INTO test VALUES ('Top.Hobbies'); 
INSERT INTO test VALUES ('Top.Hobbies.Amateurs_Astronomy'); 
INSERT INTO test VALUES ('Top.Collections'); 
INSERT INTO test VALUES ('Top.Collections.Pictures'); 
INSERT INTO test VALUES ('Top.Collections.Pictures.Astronomy'); 
INSERT INTO test VALUES ('Top.Collections.Pictures.Astronomy.Stars'); 
INSERT INTO test VALUES ('Top.Collections.Pictures.Astronomy.Galaxies'); 
INSERT INTO test VALUES ('Top.Collections.Pictures.Astronomy.Astronauts'); 
CREATE INDEX path_gist_idx ON test USING GIST (path); 
CREATE INDEX path_idx ON test USING BTREE (path); 

你可以做這樣的查詢:

ltreetest=> SELECT path FROM test WHERE path <@ 'Top.Science'; 
       path 
------------------------------------ 
Top.Science 
Top.Science.Astronomy 
Top.Science.Astronomy.Astrophysics 
Top.Science.Astronomy.Cosmology 
(4 rows) 
相關問題