樹形結構SQL結構設計

業(yè)務中經常有一些樹形結構設計,比如公司的組織架構、服務調用鏈路等,每個節(jié)點都有子節(jié)點和父節(jié)點,樹的深度又是不確定的,這時候sql結構該怎么設計呢?

1、路徑枚舉模型

在這種模型中,我們?yōu)槊總€節(jié)點存儲一個字符串路徑,表示從根節(jié)點到該節(jié)點的路徑。例如,1/3/7 表示節(jié)點7是節(jié)點3的子節(jié)點,而節(jié)點3又是節(jié)點1的子節(jié)點。

create table org (
id int(11),
name varcher(32),
parent_id int(11),
path varchar(100)
);

這種方法簡單直觀,但當樹變得很大或很深時,路徑可能會變得非常長。

1.1 查詢某個節(jié)點的所有子節(jié)點

SELECT * FROM org WHERE path LIKE '1/2/%/'
// 1/2/ 為某個節(jié)點的path路徑

1.2 查詢某個節(jié)點的所有父節(jié)點

// 查詢1/2/10的所有祖先
SELECT * FROM org WHERE "1/2/10/" LIKE path + '%' 

1.3 更新節(jié)點的路徑

//組織結構變更,
UPDATE org SET path = REPLACE(path, '1/2/', '1/2/10/') WHERE path LIKE '1/2/%';

2、閉包模型

閉包模型是一個專門為存儲樹形結構而設計的表。
包含一張節(jié)點基礎信息表,以及 一個包含節(jié)點與其所有祖先節(jié)點之間關系的關系列表;

這種方法允許你高效地查詢任何兩個節(jié)點之間的所有路徑。

create table org(
id int(11),
name varcher(32)
);

create table org_node (
id int(11),
parent_id int(11),
child_id int(11),
depth int(4) commit '從祖先到后代的深度'
);

2.1 刪除節(jié)點的所有子節(jié)點

DELETE FROM org_node WHERE parent_id = 2

2.2 查詢節(jié)點的所有子節(jié)點

SELECT n.id, n.name
FROM org n
JOIN org_node c ON n.id = c.child_id 
WHERE c.parent_id = 1 AND c.depth > 0;

2.3 新增節(jié)點

//如果要給CTO增加一個MGR2成員,則需要進行以下操作:

//1、為MGR2分配一個唯一的ID,并將其添加到nodes表中。
INSERT INTO org (name) VALUES ('MGR2'); //ID=10

//2、在closure表中插入自身的節(jié)點
INSERT INTO org_node (10, 10, 0)
//3、添加新的記錄到closure表中,以捕獲CTO與MGR2之間的關系。在此示例中,CTO是祖先,MGR2是后代,因此我們需要在org_node表中添加一條記錄,其中parent_id是CTO的ID,child_id是MGR2的ID,depth為2

INSERT INTO org_node (parend_id, child_id, depth)
SELECT a.parend_id, d.child_id, a.depth + d.depth + 1
FROM org_node a, org_node d
WHERE a.child_id = 2 AND d.parend_id = 10;

3、嵌套集模型

就是最簡單的樹形結構,org表包含節(jié)點的id、名稱等屬性。關系表org_node 用來存儲節(jié)點之間的關系,其中每一行記錄包含一個祖先節(jié)點和一個后代節(jié)點的id,它們之間建立了一條從祖先到后代的路徑。

create table org(
id int(11),
name varcher(32)
);

create table org_node (
id int(11),
parent_id int(11),
child_id int(11),
);

3.1 統(tǒng)計一個節(jié)點的所有子節(jié)點

SELECT child.*
FROM org AS parent
JOIN org_node relationships ON parent.id = relationships.parent_id
JOIN nodes AS child ON relationships.child_id= child.id
WHERE parent.id = ?

3.2 新插入節(jié)點

// 新建一個節(jié)點
INSERT INTO org (name) VALUES ('new_node');

// 添加新節(jié)點和其父節(jié)點之間的關系
INSERT INTO org_node(parent_id, child_id)
SELECT parent.id, new_node.id
FROM org  AS parent
WHERE parent.name = 'parent_node'
CROSS JOIN org  AS new_node
WHERE new_node.name = 'new_node';

3.2 查看一個節(jié)點下所有下屬的節(jié)點

//需要遞歸查詢

3.3 刪除一個節(jié)點的所有后續(xù)節(jié)點

//遞歸刪除 

4、 優(yōu)缺點對比

4.1 路徑枚舉

  • 優(yōu)點
    查詢節(jié)點的所有祖先和后代節(jié)點更高效;
    層級深度易于確定;

  • 缺點
    更新節(jié)點需要更新大量的關系數(shù)據(jù);
    數(shù)據(jù)增加時占用存儲空間更大;

數(shù)據(jù)完整性無法保證;

4.2 閉包

  • 優(yōu)點
    不需要遞歸操作;
    查詢節(jié)點的所有祖先和后代節(jié)點更高效;
    層級深度易于確定;

  • 缺點
    更新節(jié)點需要更新大量的關系數(shù)據(jù);
    層級深度大時,數(shù)據(jù)增加時占用存儲空間更大

4.3 嵌套集

  • 優(yōu)點
    簡單易懂,易于實現(xiàn);
    節(jié)點查詢和遍歷相對容易;

  • 缺點
    刪除或更新節(jié)點需要遞歸操作,性能可能不佳;
    節(jié)點的層級深度不易確定;

從工作中實踐來看,對于節(jié)點關系比較復雜的場景(比如,對于不同節(jié)點權限的控制),使用嵌套集模型是最優(yōu)的,不需要像嵌套集一樣總是遞歸操作,也不用像閉包模型一樣考慮數(shù)量太大的問題。

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容