前言
在關系型數據庫中讲竿,有一種邏輯關系比較難處理烧给,這種就是樹形結構。目前有很多主流的處理方案缝龄,比如說直接在業(yè)務表中存儲上一級id,這樣就可以用遞歸查詢SQL的形式找到某一節(jié)點的父節(jié)點挂谍,子節(jié)點叔壤,或者兄弟節(jié)點。注意口叙,是遞歸查詢炼绘!由于這種father_id的關系鍵是存儲在業(yè)務表中,那么遞歸查詢肯定對性能有影響妄田。如果業(yè)務表比較小俺亮,是可以用這種方法,表結構簡單疟呐,維護起來比較簡單脚曾,也很直觀。但是業(yè)務表如果是百萬級別的启具,這種方式就不太合適了本讥。
現在有另外一種方法,新建一個維護關系的表:閉包表鲁冯,這種方式是一種以空間換取時間的方法拷沸。下面解釋一下閉包表如何運作的。
實戰(zhàn)
假設薯演,目前存在如下樹形結構
第一步撞芍,新增一個關系表Releation
create table releation(
ancestor varchar
descendant varchar
distacne int
)
其中每一條記錄維護一段關系,圖示中的關系可以這樣維護跨扮,如下
首先序无,我們來滿足查詢验毡,即可以查詢某一個節(jié)點的父節(jié)點,子節(jié)點愉镰,等等米罚,以B節(jié)點為例子
B的所有子孫
select r.descendant from releation r where r.ancestor='B' and r.distacne>0
B的兒子
select r.descendant from releation r where r.ancestor='B' and r.distacne=1
B的第幾代(n)孫
select r.descendant from releation r where r.ancestor='B' and r.distacne=n
B的父親
select r.ancestor from releation r where r.descendant='B' and r.distacne=1
B的所有祖先
select r.ancestor from releation r where r.descendant='B' and r.distacne>0
B的兄弟也包括自己(找到B的父親,就能找到他的兄弟)
select r.descendant from releation r left join releation r1 on r1.ancestor = r.descendant
and r1.descendant='B' and r1.distacne=1
查詢所有的子節(jié)點
select ancestor max(distacne) dis from releation group by ancestor having dis=0
然后是新增節(jié)點
即為給某一個節(jié)點新增子節(jié)點丈探,假設該節(jié)點還是B,現在給B節(jié)點新增一個子節(jié)點E
首先拔莱,把自己新增進去碗降,SQL:
insert into releation values('E','E',0);
然后找到E的祖先,那么現在E的祖先是B的祖先加上B自己塘秦,然后告訴這些祖先們讼渊,他們新增了一個后代
通過找祖先的SQL,我們找到了B的祖先尊剔,A爪幻,那么E的祖先就是B和A
insert into releation values('A','E',2);
insert into releation values('B','E',1);
那么我們可以看出,新增子節(jié)點须误,除了新增自己以外挨稿,還需要通知祖先,并讓祖先保存自己京痢,下面提供一個偽碼奶甘,實現該功能
public insert(Node a,Node b){
//1.將自己記錄下來
conn.excuteSql(insert into releation values(a,a,0));
//2.查找a的祖先和自己,并告知他們祭椰,他們新增的子孫b
List<Releation> ancestors = conn.excuteSql(select r.ancestor from releation r where r.descendant=a order by distacne);
for(Releation r : ancestors){
conn.excuteSql(insert into releation values(r.ancestor,b,r.distacne+1))
}
}