關聯式資料庫處理樹狀階層(組織架構、分類目錄、BOM 物料清單)傳統上採用「遞迴 CTE(Common Table Expressions,SQL 2005+)」或「路徑列舉/巢狀集合」模型。SQL Server 2008 引入 HierarchyId 系統 CLR 類型,將階層位置作為**頭等資料類型**原生支援,簡化開發並提升效能。
HierarchyId 以變長二進位值儲存節點在樹中的**絕對路徑**。範例:
| 二進位值 | 字串表示 | 階層意義 |
|---|---|---|
| 0x | / | 根節點 |
| 0x58 | /1/ | 根節點下的第 1 個子節點 |
| 0x5AC0 | /1/1/ | 第 1 子節點下的第 1 子節點 |
字串格式為 /父/子/孫/,數字代表同層級下的排序序數(非 ID)。
CREATE TABLE OrgChart (
NodeId HIERARCHYID PRIMARY KEY,
EmpName NVARCHAR(50),
Title NVARCHAR(50)
);
-- 插入根節點
INSERT INTO OrgChart VALUES (HIERARCHYID::GetRoot(), N'CEO', N'執行長');
-- 插入子節點:取得父節點的 GetDescendant(null, null) 產生新序數
DECLARE @parent HIERARCHYID = HIERARCHYID::GetRoot();
INSERT INTO OrgChart VALUES (@parent.GetDescendant(NULL, NULL), N'CTO', N'技術長');
-- 查詢某節點的所有子孫
SELECT * FROM OrgChart
WHERE NodeId.IsDescendantOf(@parent) = 1;
-- 查詢某節點的祖先鏈
SELECT * FROM OrgChart
WHERE @node.IsDescendantOf(NodeId) = 1 AND NodeId <> @node
ORDER BY NodeId.GetLevel();
| 方法 | 回傳型別 | 用途 |
|---|---|---|
GetRoot() | HierarchyId | 取得根節點 / |
GetAncestor(n) | HierarchyId | 向上 n 層祖先 |
GetDescendant(child1, child2) | HierarchyId | 在 child1 與 child2 之間產生新子節點序數(null 表無限制) |
IsDescendantOf(parent) | BIT | 判斷是否為指定節點的子孫 |
GetLevel() | SMALLINT | 節點深度(根=0) |
ToString() | NVARCHAR(4000) | 轉為可讀路徑字串 /1/2/ |
Parse(string) | HierarchyId | 從字串解析(靜態方法) |
GetReparentedValue(oldRoot, newRoot) | HierarchyId | 將子樹從 oldRoot 移至 newRoot(移動節點核心) |
-- 將 @oldNode 整棵子樹移至 @newParent 底下
DECLARE @oldPath NVARCHAR(4000) = @oldNode.ToString();
DECLARE @newPath NVARCHAR(4000) = @newParent.GetDescendant(NULL, NULL).ToString();
UPDATE OrgChart
SET NodeId = NodeId.GetReparentedValue(
HIERARCHYID::Parse(@oldPath),
HIERARCHYID::Parse(@newPath)
)
WHERE NodeId.IsDescendantOf(@oldNode) = 1;
HierarchyId 欄位可建立叢集/非叢集索引,IsDescendantOf、GetAncestor 可利用索引求範圍。HierarchyId 值(資料列級鎖定),頻繁重組的樹不適合(建議改用遞迴 CTE 或圖資料庫)。GetDescendant 產生的序數非連續整數,插入順序決定同層排序;需業務語意排序時,應另存 SortOrder 欄位。