1 概述
樹形數據,主要關注的是:
1> 如何將數據高效地以樹形的形式展現給用戶
2> 通過某個節點找到所有的父節點。
3> 獲取某個節點的所有的后繼節點(包括子節點的子節點)
至於添加、修改、刪除和通過一個父節點獲取對應的子節點,都是可以很容易的實現。
2 鄰接模型
2.1業務:文件存放位置,在檔案管理中,需要為文件的存放位置建模,文件存在抽屜,然后抽屜在某個櫃子中,櫃子在某個房間中。
2.2表結構:
2.3備注
可以在表中再加入一個level_num字段(表示所處在樹的深度),這樣就少了那一個遞歸查詢的操作,但是在管理上有做一些處理。
2.4 測試數據

IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[Location]')) DROP TABLE [dbo].Location GO --位置表 CREATE TABLE dbo.Location ( id int, name nvarchar(50), parent_id int, order_no int ); INSERT INTO dbo.Location SELECT 1,'房間1',0,1 UNION ALL SELECT 2,'櫃子11',1,2 UNION ALL SELECT 3,'抽屜111',2,3 UNION ALL SELECT 4,'抽屜122',2,4 UNION ALL SELECT 5,'櫃子12',1,5 UNION ALL SELECT 6,'抽屜121',5,6 UNION ALL SELECT 7,'房間2',0,7 UNION ALL SELECT 8,'櫃子21',7,8 UNION ALL SELECT 9,'櫃子22',7,9 UNION ALL SELECT 10,'房間3',0,10
2.5 如何將數據高效地以樹形的形式展現給用戶,執行SQL:
;WITH locationT AS ( SELECT L.id,L.name,L.parent_id,L.order_no,0 AS levelL FROM Location AS L WHERE L.parent_id=0 UNION ALL SELECT LC.id,LC.name,LC.parent_id,LC.order_no,LP.levelL+1 FROM Location AS LC INNER JOIN locationT AS LP ON LC.parent_id=LP.id ) SELECT * , CASE levelL WHEN 0 THEN '|-'+name WHEN 1 THEN '|-|-'+name WHEN 2 THEN '|-|-|-'+name WHEN 3 THEN '|-|-|-|-'+name END AS level_name FROM locationT ORDER BY order_no
結果:
備注:其中CASE levelL WHEN 0 THEN '|-'+name WHEN 1 THEN '|-|-'+name WHEN 2 THEN '|-|-|-'+name WHEN 3 THEN '|-|-|-|-'+name END AS level_name 為樹的深度大概可知,不會很深時,可以這樣做,但是如果樹的深度不可知,可以用下面的SQL,不過效率會低一些:
SELECT * ,replace(replace(str(0,(levelL+1)),' ','0'),'0','|-')+name level_name FROM locationT ORDER BY order_no
2.6 通過某個節點找到所有的父節點,執行SQL:
;WITH locationT(id,name_level,name,parent_id,order_no,levelL) AS ( SELECT L.id,CAST(L.name AS NVARCHAR(1000)) AS name_level,L.name,L.parent_id,L.order_no,0 AS levelL FROM Location AS L WHERE L.parent_id=0 UNION ALL SELECT LC.id,CAST(LP.name_level+','+LC.name AS NVARCHAR(1000)) AS name_level,LC.name,LC.parent_id,LC.order_no,LP.levelL+1 FROM Location AS LC INNER JOIN locationT AS LP ON LC.parent_id=LP.id ) SELECT * FROM locationT WHERE name='抽屜111' ORDER BY order_no
結果:
2.7 獲取某個節點的所有子節點
;WITH locationT AS ( SELECT L.id,L.name,L.parent_id,L.order_no,0 AS levelL FROM Location AS L WHERE L.name='房間1' UNION ALL SELECT LC.id,LC.name,LC.parent_id,LC.order_no,LP.levelL+1 FROM Location AS LC INNER JOIN locationT AS LP ON LC.parent_id=LP.id ) SELECT * , CASE levelL WHEN 0 THEN '|-'+name WHEN 1 THEN '|-|-'+name WHEN 2 THEN '|-|-|-'+name WHEN 3 THEN '|-|-|-|-'+name END AS level_name FROM locationT ORDER BY order_no
結果:
3 物化路徑模型
3.1業務:文件存放位置,在檔案管理中,需要為文件的存放位置建模,文件存在抽屜,然后抽屜在某個櫃子中,櫃子在某個房間中。
3.2表結構:
3.3備注:
此時不加入parent_id也可以完成操作,但是加入parent_id可以簡化管理的操作,Level_num也可以不加入,但是加入后可以在顯示數據時簡化操作。
3.4測試數據:

IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[Location2]')) DROP TABLE [dbo].Location2 GO --位置表 CREATE TABLE dbo.Location2 ( id int, name nvarchar(50), level_code varchar(1000), parent_id int, level_num int ); INSERT INTO dbo.Location2 SELECT 1,'房間','001',0,1 UNION ALL SELECT 2,'櫃子','001.001',1,2 UNION ALL SELECT 3,'抽屜','001.001.001',2,3 UNION ALL SELECT 4,'抽屜','001.001.002',2,3 UNION ALL SELECT 5,'櫃子','001.002',1,2 UNION ALL SELECT 6,'抽屜','001.002.001',5,3 UNION ALL SELECT 7,'房間','002',0,1 UNION ALL SELECT 8,'櫃子','002.001',7,2 UNION ALL SELECT 9,'櫃子','002.002',7,2 UNION ALL SELECT 10,'房間','003',0,1
2.5 如何將數據高效地以樹形的形式展現給用戶,執行SQL:
SELECT * , CASE level_num WHEN 0 THEN '|-'+name WHEN 1 THEN '|-|-'+name WHEN 2 THEN '|-|-|-'+name WHEN 3 THEN '|-|-|-|-'+name END AS level_name FROM Location2 AS L ORDER BY level_code
結果:
2.6 通過某個節點找到所有的父節點,執行SQL:
DECLARE @level_code varchar(1000)='001.001.001'; ;WITH locationRowT AS( SELECT TOP 100 PERCENT * ,ROW_NUMBER()OVER(ORDER BY id) AS row_id FROM Location2 AS L ORDER BY level_code ) SELECT * FROM locationRowT AS LT CROSS JOIN(SELECT row_id AS start_index FROM locationRowT WHERE level_code=SUBSTRING(@level_code,1,3)) AS LTR CROSS JOIN(SELECT row_id AS end_index FROM locationRowT WHERE level_code=@level_code) AS LTC WHERE LT.row_id BETWEEN LTR.start_index AND LTC.end_index
結果:
2.7 獲取某個節點的所有子節點
SELECT * FROM Location2 AS L WHERE level_code LIKE '001%' ORDER BY level_code
結果: