image.png
1. 查詢
1.1. 得到節點 Node 下的所有節點,并按樹狀排序
SELECT * FROM tree WHERE lft BETWEEN Node.Lft AND Node.Rgt ORDER BY lft ASC;
1.2. 得到節點 Node 的路徑:
SELECT * FROM tree WHERE lft < Node.Lft AND rgt > Node.Rgt ORDER BY lft ASC;
1.3. 節點 Node 的子節點總數
(Node.Rgt-Node.Lft-1)/2
2. 修改
2.1. 增加一個節點
假設新節點的父節點是 Father,如果,這是它的第一個子節點,
nCurrent = Father.Lft;
否則,假設插入點的前一個兄弟為 Brother,
nCurrent = Brother.Rgt;
改變所有位于新節點右側的數值:
UPDATE tree SET rgt = rgt+2 WHERE rgt > nCurrent;
UPDATE tree SET lft = lft+2 WHERE lft > nCurrent;
這樣就為新插入的節點騰出了空間:
INSERT INTO tree(lft, rgt) VALUES(nCurrent+1, nCurrent+2);
2.2. 刪除一個節點 Node
DELETE Node;
UPDATE tree SET rgt = rgt - 2 WHERE rgt > Node.rgt;
UPDATE tree SET lft = lft - 2 WHERE lft > Node.rgt;
2.3. 子樹 SubTree 從 Father1 遷移至 Father2
相當于先插入子樹,再刪掉原來的子樹。
子樹根的左右值:
nSubTreeLeft = SubTree.Lft;
nSubTreeRight = SubTree.Rgt;
子樹上所有的節點數(包括子樹的根):
nSubTreeNodeNum = (nSubTreeRight - nSubTreeLeft - 1) / 2 + 1;
如果,這是 Father2 的第一個子節點,
nCurrent = Father2.Lft;
否則,假設插入點的前一個兄弟為 Brother,
nCurrent = Brother.Rgt;
騰子樹空間:
UPDATE tree SET lft=lft + 2* nSubTreeNodeNum WHERE lft > nCurrent; UPDATE tree SET rgt=rgt + 2* nSubTreeNodeNum WHERE rgt > nCurrent;
移動子樹:
UPDATE tree SET lft = lft + nCurrent + 1 - nSubTreeLeft, rgt = rgt + nCurrent + 1 - nSubTreeLeft WHERE lft BETWEEN nSubTreeLeft AND nSubTreeRight;
刪掉原來子樹占的空間:
UPDATE tree SET lft=lft -2* nSubTreeNodeNum WHERE lft > nSubTreeRight;
UPDATE tree SET rgt=rgt -2* nSubTreeNodeNum WHERE rgt > nSubTreeRight;
重構左右值
假設表結構類似:
image.png
/**
* 重構左右值
*
* @param rootId 根節點id
* @param left 左值開始值
* @return
*/
public int rebuildTree(int rootId, int left) {
int right = left + 1;
List<Category> categories = categoryMapper.selectByParentCategoryID(rootId);
for (Category category : categories) {
right = rebuildTree(category.getId(), right);
}
Category category = categoryMapper.selectByPrimaryKey(rootId);
category.setLft(left);
category.setRgt(right);
categoryMapper.updateByPrimaryKey(category);
return right + 1;
}