2014-09-29 100 views
4

我有这样的表,其中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,但又是有限的深度。

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

  • 归根结底,这已经对火鸟,MS SQL,甲骨文工作。 Oracle的CONNECT BY似乎是一种选择,但这并不能解决其他两个问题。

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

  • 速度并不重要,它是一个时间转换,我不希望客户能够得到20+的水平

  • [编辑]我刚刚发现我的第二张照片应该算级别0 1 2 3 ,而不是0 1 2 3 4 ;-)

如何计算这个嵌套级别?
或者这是从哪里来的:我如何根据树来排序?

回答

1

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

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

如果父元素的指标增加,您还可以在每个元素中保留一个级别指示符并将其增加。结果与其他行相同,但使用较少的数据。相比之下,帮助者行可用于在有效的线性时间内识别所有父母。

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 

“孩子2”和“孩子3”是不是“孩子4”和“孩子9”低 - 因为他们是后来父节点的孩子。因此,您必须携带一些代码或元数据才能使分类正常工作。总之,这种排序和格式化并不是关系型数据库的优势所在。在我看来,由于您需要连接到这么多不同的数据源,所以在应用程序层中可以更好地处理这种工作。在那里你可以灵活地与多个数据库源以及一系列树视图控件和集合进行交流,这些控件和集合已经为你想要做的迭代而构建,并具有很多其他功能。

+2

据我可以告诉火鸟唯一真正的区别是'WITH RECURSIVE'而不是'WITH' – 2014-09-30 07:27:55

0

[OP:]到目前为止的两个答案并不是答案,所以我张贴我目前的解决方案,并没有投票任何答案作为正确的(upvoting其他人的有用的建议)。

我编程德尔福在那里我有一个TClientDataSet,让我做到以下几点:

  1. 添加三个额外的(fkInternalCalc)字段TT_LEVEL(INT),TT_DATEORDER(INT),TT_SORTSTRING(串)

  2. 经过数据集,使用克隆的光标来查找父母,确定每条记录的嵌套层次,把它放在TT_LEVEL

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

  4. 对TT_LEVEL,TT_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个额外的领域

这是一个很容易理解的算法,所以也许这是非常有用的其他人也是。
请注意,通过这样做,我已'绕过'我的'没有存储过程'的要求,因为这基本上是你可以在存储过程中做的事情。
我认为这个解决方案有一个缺点,在第4步中重复父查找,但对于我的数据集来说这不是问题。


我们在程序的另一部分也实现了这个代码:当用户觉得自己已经搞得一团糟时,它很容易让用户重新排列他的树到初始状态周围的树节点;-)因此,我放弃了第4项要求:“当组节点处于同一级别,并且它们的开始日期相同时,它们的顺序是不相关的”。我现在也严格按日期排序(在步骤3中)以防止含糊不清的解决方案。

+0

这实质上是我第二个建议的一个传递。只要您的数据按照字典顺序排列,就可以开始工作。如果不是,则需要多次传递才能满足父行可能未知的级别,直到数据从树顶部过滤为止。 – Pekka 2014-10-07 10:32:24

+0

你说你不能使用存储过程,但是你可以从你的Delphi应用程序执行脚本吗? – Wodzu 2014-10-16 13:30:03

+0

@Wodzu我正在使用混合的直接SQL语句发送到服务器,并操作TClientDataset数据,更新后的SQL语句自动在“幕后”生成。 Delphi还有存储过程对象,这些对象指向RDBMS中的存储过程,但我无法使用该选项。 – 2014-10-16 13:33:34