首页 文章

如何在构建为邻接列表模型的表中确定'nesting level'

提问于
浏览
4

我有这个表,其中TT_PLAN_TASK_ID在TT_GROUP_ID中具有其父亲的ID:

enter image description here

这表示树数据如下:

enter image description here

我想按树的顺序对表数据进行排序 .
我认为如果可以按照指示计算'nesting level',那么就像在嵌套级别tt_fromdate上排序一样简单 .

思想/要求:

  • 级别数量不受限制 . 对于有限数量的级别,我可以做一些repeated joins on the same table . This approach looked nice too,但又是有限的深度 .

  • 我无法使用存储过程(然后计算嵌套级别会很容易)

  • 最终这必须适用于Firebird,MS SQL,Oracle . Oracle的CONNECT BY似乎是一个选项,但这并不能解决其他两个问题 .

  • 当组节点在同一级别并且它们的起始日期相等时,它们的顺序无关紧要(树视图中的ID 225和226都从28-4-2012开始,226可以在225之前或之后)

  • 速度并不重要,它是一次性转换,我不认为客户有20个级别

  • [编辑]我只是注意到我的第二张照片应该是0 1 2 3,而不是0 1 2 3 4 ;-)

如何计算此嵌套级别?
或者它来自何处:我如何根据树进行排序?

3 回答

  • 1

    您需要使用递归查询来标识和标记未知数量的级别 . 这是一个如何用tsql(sql-server)语法生成它的示例 . 我留给你来制定其他数据库查询,但所有技术都有一个合理的等价物 .

    WITH tmpCTE (all_other_fields, TT_PLAN_TASK_ID, TT_GROUP_ID, [level]) as
    (
    
        SELECT all_other_fields, TT_PLAN_TASK_ID, TT_GROUP_ID, 0 as [level]
        FROM #myTable
        WHERE TT_GROUP_ID = 0
    
        UNION ALL
    
        SELECT t.all_other_fields, t.TT_PLAN_TASK_ID, t.TT_GROUP_ID, [level] + 1
            FROM #myTable t
                INNER JOIN tmpCTE cte
                    ON t.TT_GROUP_ID = cte.TT_PLAN_TASK_ID
    
    )
    
    SELECT * FROM tmpCTE order by level
    

    但是,我认为你会发现这还不足以产生你想要创建的订单 . 您需要发送某种“完整地址”,其中包含可排序值的整个“级别祖先”行 . 考虑这个清单

    PARENT - A
     child 1
     child 4
     child 9
    
    PARENT - B
     child 2
     child 3
    

    'child 2'和'child 3'低于'child 4'和'child 9' - 因为他们是后来父节点的孩子 . 因此,您必须携带某种代码或元数据才能使排序正常工作 .

    总而言之,这种排序和格式化并不是关系数据库的强项 . 在我看来,因为你需要连接到这么多不同的数据源,所以在应用层中可以更好地处理这种工作 . 在那里,您可以灵活地与多个数据库源以及一系列树视图控件和集合进行对话,这些控件和集合已经为您想要进行的迭代构建 - 以及大量附加功能 .

  • 2

    如果您可以静态绑定级别,那么展开所有可能性的查询将起作用 .

    这不是很优雅,可以通过添加标识更高级别的帮助行来解决 . 如果您遍历表并将表中的每对相关行(级别0级别1,级别1级别2)的行插入单个行(级别0级别2),那么当迭代不再添加行时,然后 Build 每个从上到下的链接并可以使用 .

    您还可以在每个元素中保留一个级别指示符,并在父元素的指示符增加时递增 . 结果与其他行相同,但使用的数据更少 . 相反,辅助行可用于在有效线性时间内识别所有父项 .

  • 0

    [OP:]到目前为止,这两个答案并非不是答案,所以我发布了我当前的解决方案而没有将任何答案作为正确的答案(对其他人提出有用的建议) .

    我在Delphi中编程,我有一个TClientDataset,它允许我执行以下操作:

    • 添加三个额外(fkInternalCalc)字段TT_LEVEL(int),TT_DATEORDER(int),TT_SORTSTRING(string)

    • 浏览数据集,使用克隆光标查找父级,确定每条记录的嵌套级别,将其放入TT_LEVEL

    • 对TT_LEVEL,TT_FROMDATE,TT_PLAN_TASK_ID上的数据进行排序,然后为每个级别填充TT_DATEORDER,并使用整数值0,1,...我们基本上只是将日期转换为其顺序,并且在排序顺序中包括TT_PLAN_TASK_ID只是为了当同一级别的两个记录具有相同的开始日期时,保证明确的顺序

    • 对TT_LEVEL,TT_DATEORDER上的数据进行排序 . 再次浏览数据集 . 将级别日期顺序放在TT_SORTSTRING中 . 现在再次找到所有父母,并将他们的等级日期顺序添加到TT_SORTSTRING . 你最终会得到像'00 -00-01-00-02-01'这样的东西,意思是(从右到左)'等级2,日期顺序1'和父级'等级1,dateorder 0',父级为'0级,日期顺序为0' . 确保字符串具有相同的长度,连字符仅用于可读性 .

    • 在TT_SORTSTRING上排序,填写TT_TASKORDER 0,1,2 ......

    • 扔掉3个额外的字段

    这是一个易于理解的算法,所以它也可能对其他人有用 .
    请注意,通过执行此操作,我有'circumvented'我的'no stored procedures'要求,因为这基本上是您可以在存储过程中执行的操作 .
    我认为这个解决方案有一个缺点,在步骤4中重复父查找,但对于我的数据集,这不是问题 .

    [添加]
    我们已经在程序的另一部分中实现了这个代码:当用户觉得他拖累了所有树节点时,它可以很容易地将他的树重新排序到初始状态;-)所以我已经放弃了第4项要求"When group nodes are on the same level and their starting dates are equal, their order is irrelevant" . 我现在也严格按日期排序(在步骤3中)以防止模棱两可的解决方案 .

相关问题