【问题标题】:SQL: Hierarchical structure with n levelSQL:具有n级的层次结构
【发布时间】:2016-05-25 14:41:59
【问题描述】:

我有一个存储在关系数据库中的层次结构,它以树形视图表示。 每个节点都有其属性的各种字段,并通过 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 中的每个级别中有很多节点,其中有一个层次结构 ID 数据类型的列路径。然后你不想查询(初始化 TreeView)所有具有至少一个后代(必须不是直接后代,descandant 可以具有每个可能的级别)的顶级节点,这些节点适用于过滤器(例如:名称LIKE '%motor%' AND type = 3,其中名称和类型是同一路径模型表中的列)。我们还存储了节点的从零开始的级别以简化查询。

查询可能是:

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

这个查询可能性能一般,但正如您所见,在顶级查询中您也有一个昂贵的子查询,它必须从表中查询符合条件的所有节点。

但是如果用户点击小 [+] 来展开一个顶级节点,您必须查询所有二级节点,将点击的节点作为祖先并且还匹配该过滤条件(包含任何级别的 descandants,即火柴)。如果一个节点本身符合过滤条件(类型 3 并且名称包括“电机”),那么您必须显示它的所有后代。

这些查询在我们的示例中表现不佳。


是否有任何其他模型您可以更喜欢或一些想法以获得更好的性能。

谢谢!

【问题讨论】:

  • 您有一个示例说明为什么不能使用嵌套集模型进行过滤吗?实际上,在该模型中按后代过滤似乎非常简单。
  • 据我所知,您可以轻松检查节点是否有后代,是的。但是我们必须检查一个节点是否有符合某些过滤条件的后代。只要嵌套集模型的 Left 和 Right 值提供有关是否包含 descandants 的反馈,并且如果我过滤掉一些行,它们就不会改变,就没有办法。我说的对吗?
  • 您当然可以使用嵌套集和过滤。它实际上完全按照您用文字描述的那样工作:选择每个具有后代的节点(这意味着:某个节点的左/右在节点的左右之间)或它本身满足过滤条件:select * from node where exists (select * from nodes as n where "filtercriteria for your nodes" and node.left <= n.left and node.right >= n.right)
  • mysql 还是 sql-server?你已经用这两个标记了这个问题。
  • 其实是 SQL-Server

标签: mysql sql sql-server treeview hierarchical-data


【解决方案1】:

二十多年来,我一直在为我的层次结构使用范围键。我们有大量的替代层次结构,用于报告、处理和/或选择标准。我还创建了一个用于快速导航和实用程序的函数库。

以下是一个快速示例。请记住,我手动创建了范围键,它们通常以编程方式创建/更新。另外,我通常有一个演示序列号,用于在归因过程中逐级控制实际序列。

真正的美妙之处在于,您无需使用递归查询即可轻松聚合可变深度数据。

下面的查询缺少我的所有助手,因为我想说明这项技术。

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

返回

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

【讨论】:

    【解决方案2】:

    自 SQL Server 2005 起,T-SQL 开发人员可以执行recursive queries with CTE structures for hierarchy data

    如果您查看参考的 SQL 教程,您会在最后一个屏幕截图中找到示例数据和示例 CTE 查询以及层次结构级别

    递归CTE在SQL Server中形成如下

    WITH cte AS (
       {Anchor_Query}
       UNION ALL
       {Recursive part joining to cte}
     )
     SELECT * FROM cte
    

    通过在 CTE 中的锚选择语句中将层次结构的初始级别添加为 1,并在递归部分将其增加 1,您最终将在结果数据集中得到所有层次结构级别

    请查看示例 SQL 教程

    【讨论】:

      【解决方案3】:

      使用嵌套集模型,查找所有具有名为“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 语句来获取子节点。

      【讨论】:

        【解决方案4】:

        非常感谢您的努力。你真的很有帮助。 我们提出了 Path-Model 和递归 CTE 的组合,并在表中额外插入了父 id 以获得一点额外的性能提升。

        【讨论】:

          猜你喜欢
          • 2014-07-23
          • 2013-11-07
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多