SQL: n 级层次结构
SQL: Hierarchical structure with n level
我在关系数据库中存储了一个层次结构,该结构以树视图表示。
每个节点都有其属性的各种字段,并通过 ID 知道其父节点。
这是亲子关系模型。
如果节点有子节点,则在节点名称前用 [+] 表示。通过单击 [+],您可以展开节点并查看子节点。
如果子节点有最低级别的子节点,则子节点本身有一个 [+]。
一个简化的示例树视图如下所示:
[+] A Land
[+] A.1 Car
A.1.A Motor
A.1.B Wheels
[+] B Sea
B.1 Sailing ship
[+] B.2 Motorboat
B.2.A Motor
[+] C Air
[+] C.1 Plane
C.1.A Turbine
C.2.B Wheels
可以在各种节点属性上设置一个或多个过滤器,例如显示所有具有名称为 'Motor' 的后代的节点。
树视图看起来像:
[+] A Land
[+] A.1 Car
A.1.A Motor
[+] B Sea
[+] B.2 Motorboat
B.2.A Motor
因为我的关卡数量有限,节点数量也很少,所以这个结构满足了我的需求(性能一般)。
现在我们要将树视图扩展到 n 级深度。
有嵌套集模型,只要不过滤掉东西,性能就很好。这是因为据我们所知,嵌套集不支持过滤。我们还尝试了路径模型(SQL-Servers hierarchyid-datatype),但是如果你有很多级别,过滤速度很慢。
我们的路径模型方法:
假设您有 20 个级别,在 tbale PMTable 的每个级别中都有很多节点,其中有一列路径为 hierarchyid-datatype。然后你不想查询(初始化 TreeView)所有至少有一个后代的顶级节点(必须不是直接后代,descandant 可以有每个可能的级别),它适用于过滤器(例如:名称LIKE '%motor%' AND type = 3,其中名称和类型是同一路径模型中的列 table)。我们还存储了节点的从零开始的级别,以简化查询。
查询可以是:
SELECT id, name
FROM PMTable WHERE level = 0
AND Path IN
(
SELECT Path WHERE Path.GetAncestor(Path.GetLevel() - 1)
FROM PMTable
WHERE name LIKE '%motor%' AND type = 3
)
ORDER BY name
此查询的性能可能很一般,但如您所见,在顶级查询中您还有一个开销很大的子查询,它必须查询 table 中符合条件的所有节点。
但是如果用户点击小[+]展开一个顶级节点,你必须查询所有二级节点,以点击的节点为祖先并且也匹配该过滤条件(包含任何级别的descandants ,即匹配)。如果节点本身匹配该过滤条件(类型 3 且名称包含 'motor'),则您必须显示其所有后代。
这些查询在我们的示例中性能不佳。
是否有您喜欢的其他模型或为此获得更好性能的一些想法。
谢谢!
从 SQL Server 2005 开始,T-SQL 开发人员能够执行 recursive queries with CTE structures for hierarchy data
如果您查看参考的 SQL 教程,您将在最后一个屏幕截图中找到示例数据和示例 CTE 查询以及层次结构级别
在SQLServer
中形成如下递归CTE
WITH cte AS (
{Anchor_Query}
UNION ALL
{Recursive part joining to cte}
)
SELECT * FROM cte
通过在 CTE 的锚 Select 语句中将层次结构的初始级别添加为 1,并在递归部分将其增加 1,您最终将在结果数据集中得到所有层次结构级别
请查看示例SQL教程
2 多年来,我一直在为我的层次结构使用范围键。我们有大量的替代层次结构,用于报告、处理、and/or 选择标准。我还创建了一个用于快速导航和实用程序的函数库。
以下是一个快速示例。请记住,我手动创建了范围键,它们通常以编程方式 created/updated 。另外,我通常有一个Presentation Sequence number来控制属性期间逐级的实际顺序。
真正的美妙之处在于您可以轻松聚合可变深度数据,而无需使用递归查询。
下面的查询缺少我的所有助手,因为我想说明该技术。
Declare @OH table (OH_R1 int,OH_R2 int,OH_Lvl int,OH_Nr int,OH_Pt int,OH_Title varchar(100))
Insert into @OH Select 0,12,1,9,0,'Total'
Insert into @OH Select 1,4,2,100,9,'Land'
Insert into @OH Select 2,4,3,200,100,'Car'
Insert into @OH Select 3,3,4,300,200,'Motor'
Insert into @OH Select 4,4,4,400,200,'Wheels'
Insert into @OH Select 5,8,2,500,9,'Sea'
Insert into @OH Select 6,6,3,600,500,'Sailing Ship'
Insert into @OH Select 7,8,3,625,500,'Motor Boat'
Insert into @OH Select 8,8,4,650,625,'Motor'
Insert into @OH Select 9,12,2,800,9,'Air'
Insert into @OH Select 10,12,3,825,800,'Plane'
Insert into @OH Select 11,11,4,550,825,'Turbine'
Insert into @OH Select 12,12,4,550,825,'Wheele'
-- Show Nested/Filtered Hierarchy
Select A.*
,Nested=Replicate(' ',OH_Lvl-1)+OH_Title
,Hits=sum(hits)
From @OH A
Join (Select OH_R1,Hits=1 from @OH where OH_Title like '%motor%' and OH_Lvl=4) B on (B.OH_R1 between A.OH_R1 and A.OH_R2)
Group by A.OH_R1,A.OH_R2,A.OH_Lvl,A.OH_Nr,A.OH_Pt,A.OH_Title
Order by OH_R1
-- Show Actual Hierarchy
Select * from @OH Order by OH_R1
Returns
OH_R1 OH_R2 OH_Lvl OH_Nr OH_Pt OH_Title Nested Hits
0 12 1 9 0 Total Total 2
1 4 2 100 9 Land Land 1
2 4 3 200 100 Car Car 1
3 3 4 300 200 Motor Motor 1
5 8 2 500 9 Sea Sea 1
7 8 3 625 500 Motor Boat Motor Boat 1
8 8 4 650 625 Motor Motor 1
使用嵌套集模型,查找所有具有名称为 "Motor" 的后代的树应该非常简单:
SELECT
P.name -- Or whatever other columns you need
FROM
My_Tree D
INNER JOIN My_Tree P ON P.lft <= D.lft AND P.rgt >= D.rgt
WHERE
D.name = 'Motor'
如果您还想包括该节点的后代(即任何成员都具有该名称的完整树),那么您也可以轻松添加 OR
语句来获取子节点。
非常感谢您的努力。你真的很有帮助。
我们提出了路径模型和递归 CTE 的组合,并在 table 中额外插入父 ID 以获得一点额外的性能提升。
我在关系数据库中存储了一个层次结构,该结构以树视图表示。 每个节点都有其属性的各种字段,并通过 ID 知道其父节点。 这是亲子关系模型。
如果节点有子节点,则在节点名称前用 [+] 表示。通过单击 [+],您可以展开节点并查看子节点。 如果子节点有最低级别的子节点,则子节点本身有一个 [+]。
一个简化的示例树视图如下所示:
[+] A Land
[+] A.1 Car
A.1.A Motor
A.1.B Wheels
[+] B Sea
B.1 Sailing ship
[+] B.2 Motorboat
B.2.A Motor
[+] C Air
[+] C.1 Plane
C.1.A Turbine
C.2.B Wheels
可以在各种节点属性上设置一个或多个过滤器,例如显示所有具有名称为 'Motor' 的后代的节点。 树视图看起来像:
[+] A Land
[+] A.1 Car
A.1.A Motor
[+] B Sea
[+] B.2 Motorboat
B.2.A Motor
因为我的关卡数量有限,节点数量也很少,所以这个结构满足了我的需求(性能一般)。
现在我们要将树视图扩展到 n 级深度。
有嵌套集模型,只要不过滤掉东西,性能就很好。这是因为据我们所知,嵌套集不支持过滤。我们还尝试了路径模型(SQL-Servers hierarchyid-datatype),但是如果你有很多级别,过滤速度很慢。
我们的路径模型方法: 假设您有 20 个级别,在 tbale PMTable 的每个级别中都有很多节点,其中有一列路径为 hierarchyid-datatype。然后你不想查询(初始化 TreeView)所有至少有一个后代的顶级节点(必须不是直接后代,descandant 可以有每个可能的级别),它适用于过滤器(例如:名称LIKE '%motor%' AND type = 3,其中名称和类型是同一路径模型中的列 table)。我们还存储了节点的从零开始的级别,以简化查询。
查询可以是:
SELECT id, name
FROM PMTable WHERE level = 0
AND Path IN
(
SELECT Path WHERE Path.GetAncestor(Path.GetLevel() - 1)
FROM PMTable
WHERE name LIKE '%motor%' AND type = 3
)
ORDER BY name
此查询的性能可能很一般,但如您所见,在顶级查询中您还有一个开销很大的子查询,它必须查询 table 中符合条件的所有节点。
但是如果用户点击小[+]展开一个顶级节点,你必须查询所有二级节点,以点击的节点为祖先并且也匹配该过滤条件(包含任何级别的descandants ,即匹配)。如果节点本身匹配该过滤条件(类型 3 且名称包含 'motor'),则您必须显示其所有后代。
这些查询在我们的示例中性能不佳。
是否有您喜欢的其他模型或为此获得更好性能的一些想法。
谢谢!
从 SQL Server 2005 开始,T-SQL 开发人员能够执行 recursive queries with CTE structures for hierarchy data
如果您查看参考的 SQL 教程,您将在最后一个屏幕截图中找到示例数据和示例 CTE 查询以及层次结构级别
在SQLServer
中形成如下递归CTEWITH cte AS (
{Anchor_Query}
UNION ALL
{Recursive part joining to cte}
)
SELECT * FROM cte
通过在 CTE 的锚 Select 语句中将层次结构的初始级别添加为 1,并在递归部分将其增加 1,您最终将在结果数据集中得到所有层次结构级别
请查看示例SQL教程
2 多年来,我一直在为我的层次结构使用范围键。我们有大量的替代层次结构,用于报告、处理、and/or 选择标准。我还创建了一个用于快速导航和实用程序的函数库。
以下是一个快速示例。请记住,我手动创建了范围键,它们通常以编程方式 created/updated 。另外,我通常有一个Presentation Sequence number来控制属性期间逐级的实际顺序。
真正的美妙之处在于您可以轻松聚合可变深度数据,而无需使用递归查询。
下面的查询缺少我的所有助手,因为我想说明该技术。
Declare @OH table (OH_R1 int,OH_R2 int,OH_Lvl int,OH_Nr int,OH_Pt int,OH_Title varchar(100))
Insert into @OH Select 0,12,1,9,0,'Total'
Insert into @OH Select 1,4,2,100,9,'Land'
Insert into @OH Select 2,4,3,200,100,'Car'
Insert into @OH Select 3,3,4,300,200,'Motor'
Insert into @OH Select 4,4,4,400,200,'Wheels'
Insert into @OH Select 5,8,2,500,9,'Sea'
Insert into @OH Select 6,6,3,600,500,'Sailing Ship'
Insert into @OH Select 7,8,3,625,500,'Motor Boat'
Insert into @OH Select 8,8,4,650,625,'Motor'
Insert into @OH Select 9,12,2,800,9,'Air'
Insert into @OH Select 10,12,3,825,800,'Plane'
Insert into @OH Select 11,11,4,550,825,'Turbine'
Insert into @OH Select 12,12,4,550,825,'Wheele'
-- Show Nested/Filtered Hierarchy
Select A.*
,Nested=Replicate(' ',OH_Lvl-1)+OH_Title
,Hits=sum(hits)
From @OH A
Join (Select OH_R1,Hits=1 from @OH where OH_Title like '%motor%' and OH_Lvl=4) B on (B.OH_R1 between A.OH_R1 and A.OH_R2)
Group by A.OH_R1,A.OH_R2,A.OH_Lvl,A.OH_Nr,A.OH_Pt,A.OH_Title
Order by OH_R1
-- Show Actual Hierarchy
Select * from @OH Order by OH_R1
Returns
OH_R1 OH_R2 OH_Lvl OH_Nr OH_Pt OH_Title Nested Hits
0 12 1 9 0 Total Total 2
1 4 2 100 9 Land Land 1
2 4 3 200 100 Car Car 1
3 3 4 300 200 Motor Motor 1
5 8 2 500 9 Sea Sea 1
7 8 3 625 500 Motor Boat Motor Boat 1
8 8 4 650 625 Motor Motor 1
使用嵌套集模型,查找所有具有名称为 "Motor" 的后代的树应该非常简单:
SELECT
P.name -- Or whatever other columns you need
FROM
My_Tree D
INNER JOIN My_Tree P ON P.lft <= D.lft AND P.rgt >= D.rgt
WHERE
D.name = 'Motor'
如果您还想包括该节点的后代(即任何成员都具有该名称的完整树),那么您也可以轻松添加 OR
语句来获取子节点。
非常感谢您的努力。你真的很有帮助。 我们提出了路径模型和递归 CTE 的组合,并在 table 中额外插入父 ID 以获得一点额外的性能提升。