層次結構數據定義為一組通過層次結構關係互相關聯的數據項。 在層次結構關係中,一個數據項是另一個項的父級或子級。 sql server2008開始內置的 hierarchyid 數據類型使存儲和查詢層次結構數據變得更為容易。hierarchyid 其實是 CLR 數據類型。 廢話不多說,看不明白就實際 ...
層次結構數據定義為一組通過層次結構關係互相關聯的數據項。 在層次結構關係中,一個數據項是另一個項的父級或子級。
sql server2008開始內置的 hierarchyid 數據類型使存儲和查詢層次結構數據變得更為容易。hierarchyid 其實是 CLR 數據類型。
廢話不多說,看不明白就實際操作一把,然後再回過頭來理解。
下麵先創建一個表,並插入一些數據:
create table RoleMan ( NodeId hierarchyid not null, RoleId int not null, RoleName nvarchar(32) not null, Par_NodeId as NodeId.GetLevel() -- GetLevel()用於確定當前層次的深度(級別),最頂層(根節點)為0,然後依次加1。 ) go insert into RoleMan(NodeId,RoleId,RoleName) select '/1/','1','超級管理員' union select '/1/1/','2','管理員A' union select '/1/2/','3','管理員B' union select '/1/1/1/','4','用戶AA' union select '/1/1/2/','5','用戶AB' union select '/1/2/1/','6','用戶BA' union select '/1/2/2/','7','用戶BB' go select *, NodeId.ToString() NodeId_Path -- 因為 hierarchyid 類型的值是以16進位表示的,這裡把他轉換為字元串 from RoleMan
查詢指定節點的祖先節點:
-- 查詢指定節點的祖先節點 declare @NodeId hierarchyid select @NodeId=NodeId from RoleMan where RoleId=5 select *,NodeId.ToString() NodeId_Path from RoleMan where @NodeId.IsDescendantOf(NodeId)=1 -- IsDescendantOf(NodeId),判斷指定節點是否是另一個節點的後代,如果是,則返回1
查詢指定節點的子孫節點:
-- 查詢指定節點的子孫節點 declare @NodeId hierarchyid select @NodeId=NodeId from RoleMan where RoleId=2 select *,NodeId.ToString() NodeId_Path from RoleMan where NodeId.IsDescendantOf(@NodeId)=1 -- IsDescendantOf(NodeId),判斷指定節點是否是另一個節點的後代,如果是,則返回1
返回指定層級的所有節點:
-- 返回指定層級的所有節點 declare @NodeId hierarchyid select @NodeId=NodeId from RoleMan where Par_NodeId=1 -- 指定層級為 1 級 select @NodeId select *,NodeId.ToString() NodeId_Path from RoleMan where NodeId.GetAncestor(0)=@NodeId -- GetAncestor(0),會返回當前層級當前節點的數據 select @NodeId select *,NodeId.ToString() NodeId_Path from RoleMan where NodeId.GetAncestor(1)=@NodeId -- GetAncestor(1),會返回指定層級(@NodeId指定為1級)的下一級的所有節點的數據 -- 數值 1 表示要在層次結構中下降的級別數。
插入新節點:
declare @PNodeId hierarchyid declare @NodeId hierarchyid select @PNodeId=NodeId from RoleMan where RoleId=3 -- 獲取 管理員B 的節點,即用於為添加的節點指定父級 select @NodeId=NodeId from RoleMan where RoleId=7 -- 獲取 用戶BB 的節點,即指定添加的節點位於哪個子節點後面 insert into RoleMan(NodeId,RoleId,RoleName) values(@PNodeId.GetDescendant(@NodeId, NULL),'8','用戶BC') --即在父節點為 '管理員B' 下麵的子節點 '用戶BB' 後面添加一個節點 '用戶BC' select *, NodeId.ToString() NodeId_Path from RoleMan
當然,這是父節點下麵存在著子節點的時候,那麼當父節點下麵沒有子節點應該怎麼添加呢?只需要將 GetDescendant(null,null) 的兩個參數都設置為null就行了。如下:
declare @PNodeId hierarchyid select @PNodeId=NodeId from RoleMan where RoleId=8 -- 獲取 用戶BC 的節點,即用於為添加的節點指定父級 insert into RoleMan(NodeId,RoleId,RoleName) values(@PNodeId.GetDescendant(null, NULL),'9','用戶BCA') -- 為無子節點的父節點添加子節點 select *, NodeId.ToString() NodeId_Path from RoleMan
如果需要在一個父節點下麵的兩個子節點之間插入一個子節點,就需要將 GetDescendant(@Child1,@Child2) 的兩個參數同時指定。如下:
declare @PNodeId hierarchyid declare @Child1 hierarchyid declare @Child2 hierarchyid select @PNodeId=NodeId from RoleMan where RoleId=2 -- 獲取 管理員A 的節點,即用於為添加的節點指定父級 select @Child1=NodeId from RoleMan where RoleId=4 -- 獲取第一個子節點 select @Child2=NodeId from RoleMan where RoleId=5 -- 獲取第二個子節點 insert into RoleMan(NodeId,RoleId,RoleName) values(@PNodeId.GetDescendant(@Child1, @Child2),'10','用戶A插隊')-- 在父節點 管理員A 的子節點 用戶AA 和 用戶AB 之間插入一個節點 用戶A插隊 select *, NodeId.ToString() NodeId_Path from RoleMan
變更節點位置:
變更節點位置應當使用 GetReparentedValue 方法,該方法接受兩個參數,一個是原節點的 hierarchyid,另一個是目標節點 hierarchyid。
-- 把 管理員B 節點下麵的子節點 用戶BA 移動到 管理員A 節點的子節點 用戶AB 後面 declare @RawNodePath hierarchyid declare @NewNodePath hierarchyid select @RawNodePath=NodeId from RoleMan where RoleId=6 -- 獲取節點 用戶BA select @NewNodePath=NodeId from RoleMan where RoleId=2 -- 獲取節點 管理員A select @NewNodePath=@NewNodePath.GetDescendant(MAX(NodeId), NULL) -- 獲取節點 管理員A 下麵的最大的子節點,即最後一個子節點 from RoleMan where NodeId.GetAncestor(1)=@NewNodePath -- 獲取父節點 管理員A 下麵的所有子級 update RoleMan set NodeId=NodeId.GetReparentedValue(@RawNodePath, @NewNodePath) where NodeId.IsDescendantOf(@RawNodePath) = 1 select *, NodeId.ToString() NodeId_Path from RoleMan go
hierarchyid 函數:
GetLevel():用於確定當前層次的深度(級別),最頂層(根節點)為0,然後依次加1。
ToString():因為 hierarchyid 類型的值是以16進位表示的,ToString()用於將 hierarchyid 類型轉換為字元串類型。
IsDescendantOf():判斷指定節點是否是另一個節點的後代,如果是,則返回1。一個參數,為指定的節點。
GetAncestor(n):n=0時,會返回當前層級當前節點的數據。否則,會返回指定層級的下 n 級的所有節點。
GetDescendant(null,null) :用於添加節點,該方法接受兩個參數,可為空,第一個子節點,第二個子節點。如果第一個參數不為空,表示在指定的父節點下麵的子節點後面添加節點,如果兩個參數皆為空,則表示要在沒有子節點的節點添加節點。
GetReparentedValue():用於變更節點位置,該方法接受兩個參數,一個是原節點的hierarchyid,另一個是目標節點hierarchyid。
GetRoot():獲取節點的根。
Parse():將字元串轉換為 hierarchyid 。該字元串的格式通常都是/1/這樣的。
Read():Read 從傳入的 BinaryReader 讀取 SqlHierarchyId 的二進位表示形式,並將 SqlHierarchyId 對象設置為該值。不能使用 Transact-SQL 調用 Read。請改為使用 CAST 或 CONVERT。
Write():Write 將 SqlHierarchyId 的二進位表示形式寫出到傳入的 BinaryWriter 中。無法通過使用 Transact-SQL 來調用 Write。請改為使用 CAST 或 CONVERT。
hierarchyid 索引策略:
用於對層次結構數據進行索引的策略有兩種:深度優先和廣度優先。
深度優先索引,子樹中各行的存儲位置相鄰,簡而言之,就是以 hierarchyid 值排序的方式存儲。
創建深度優先索引:
--創建深度優先索引 create unique index Role_Depth_First on RoleMan(NodeId) go select *,NodeId.ToString() NodeId_Path from RoleMan order by NodeId
廣度優先索引,是將層次結構中每個級別的各行存儲在一起,簡而言之,就是按層級排序的方式存儲。
創建廣度優先索引:
--創建廣度優先索引 create clustered index Role_Breadth_First on RoleMan(Par_NodeId,NodeId) ; go select *,NodeId.ToString() NodeId_Path from RoleMan order by Par_NodeId,NodeId
參考:
http://blog.csdn.net/zhanghongju/article/details/42584643
https://msdn.microsoft.com/zh-cn/library/bb677173.aspx