如何在构建为邻接列表模型的表中确定“嵌套级别”

时间:2014-09-29 15:00:56

标签: sql sql-server oracle nested firebird

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

enter image description here

这表示树数据如下:

enter image description here

我想按照树的顺序对表数据进行排序 我认为如果可以计算出''嵌套等级'如上所示,它就像在嵌套级别+ tt_fromdate上排序一样简单。

思想/要求:

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

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

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

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

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

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

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

3 个答案:

答案 0 :(得分:2)

您需要使用递归查询来识别和标记未知数量的级别。这是一个如何用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' - 因为它们是后来父节点的子节点。因此,您必须携带某种代码或元数据才能使排序正常工作。

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

答案 1 :(得分:1)

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

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

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

答案 2 :(得分:0)

[OP:]到目前为止,这两个答案并非 答案,所以我发布了我当前的解决方案,并没有将任何答案投票给正确的答案(赞同其他答案以获得有用的建议) )。

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

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

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

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

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

  5. 按TT_SORTSTRING排序,填写TT_TASKORDER 0,1,2 ...

  6. 丢掉3个额外的字段

  7. 这是一个易于理解的算法,所以也许它对其他人也很有用 请注意,通过这样做,我“规避了”我的“无存储过程”要求,因为这基本上是您可以在存储过程中执行的操作。
    我认为这个解决方案有一个缺点,在步骤4中重复父查找,但对于我的数据集来说这不是问题。

    [补充]
    我们已经在程序的另一部分中实现了这个代码:当用户觉得他拖累了所有树节点时,它可以很容易地将他的树重新排序到初始状态;-)所以我已经放弃了第4项要求“当组节点处于同一级别并且它们的起始日期相同时,它们的顺序无关紧要”。我现在也严格按日期排序(在步骤3中)以防止模棱两可的解决方案。