常見數據庫設計(4)——樹形結構數據


1 概述
樹形數據,主要關注的是:
1> 如何將數據高效地以樹形的形式展現給用戶
2> 通過某個節點找到所有的父節點。
3> 獲取某個節點的所有的后繼節點(包括子節點的子節點)
至於添加、修改、刪除和通過一個父節點獲取對應的子節點,都是可以很容易的實現。

2 鄰接模型
2.1業務:文件存放位置,在檔案管理中,需要為文件的存放位置建模,文件存在抽屜,然后抽屜在某個櫃子中,櫃子在某個房間中。
2.2表結構:

2.3備注
可以在表中再加入一個level_num字段(表示所處在樹的深度),這樣就少了那一個遞歸查詢的操作,但是在管理上有做一些處理。

2.4 測試數據

View Code
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測試數據:

View Code
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

結果:

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM