Sql server 在SQL Server中展平时变层次结构并获取每对之间的距离

Sql server 在SQL Server中展平时变层次结构并获取每对之间的距离,sql-server,sql-server-2012,common-table-expression,Sql Server,Sql Server 2012,Common Table Expression,我试图通过获取每个ID和树中每个祖先之间的距离来压平一棵树。感谢stackoverflow,虽然这本身并不难,但我一直很难创建一个CTE来恰当地处理树随时间变化的事实 我试图获得树中任意给定时间点的每个ID/祖先对的列表,以及该ID与祖先之间的距离 有没有SQL Server向导知道该怎么做?因为我一直在尝试,我总是得到明显错误的值 样本数据 在: 输出: 下面是我创建的一个示例-可能不是很漂亮,但它似乎有效。我唯一不明白的是:对于带有AncestorID 1的ID 5,您的第二个输出来自哪里

我试图通过获取每个ID和树中每个祖先之间的距离来压平一棵树。感谢stackoverflow,虽然这本身并不难,但我一直很难创建一个CTE来恰当地处理树随时间变化的事实

我试图获得树中任意给定时间点的每个ID/祖先对的列表,以及该ID与祖先之间的距离

有没有SQL Server向导知道该怎么做?因为我一直在尝试,我总是得到明显错误的值

样本数据

在:

输出:


下面是我创建的一个示例-可能不是很漂亮,但它似乎有效。我唯一不明白的是:对于带有AncestorID 1的ID 5,您的第二个输出来自哪里

然而,到目前为止,我得到的是:

DECLARE @t TABLE (ID int, AncestorID int, StartDate date, EndDate date);
INSERT INTO @t VALUES (2, 1, '2000-01-01', '2000-01-04')
                     ,(3, 1, '2000-01-01', '2000-01-02')
                     ,(4, 3, '2000-01-01', '2000-01-02')
                     ,(5, 4, '2000-01-01', '2000-01-02')
                     ,(4, 1, '2000-01-03', '2000-01-04')
                     ,(3, 4, '2000-01-03', '2000-01-04');

DECLARE @MinAncestorID int = (SELECT min(AncestorID) FROM @t);

WITH cteBase AS(
  SELECT @MinAncestorID ID, 0 AncestorID, CONVERT(date, '1900-01-01') StartDate, CONVERT(date, '2999-12-31') EndDate
  UNION ALL
  SELECT t.ID, t.AncestorID, t.StartDate, t.EndDate
    FROM @t t
    JOIN cteBase c ON t.AncestorID = c.ID AND t.StartDate >= c.StartDate AND t.EndDate <= c.EndDate
),
cte2 AS(
SELECT ID, AncestorID, StartDate, EndDate
  FROM cteBase
  WHERE AncestorID > 0
UNION ALL
SELECT b.ID, a.AncestorID, a.StartDate, a.EndDate
  FROM cteBase AS b
  JOIN cte2 AS a ON a.ID = b.AncestorID AND a.StartDate >= b.StartDate AND a.EndDate <= b.EndDate
)
SELECT * FROM cte2
ORDER BY 1, 2
OPTION (MAXRECURSION 0)

您的最后一行ID正确吗?因为在第三行ID=4中,AncestorID是3,在最后一行ID=3中,AncestorID是4,我不认为这在逻辑上是正确的,这就是为什么它会成为一个麻烦。4的两个条目适用于不同的日期。4->3在1号和2号上,4->1在3号和4号上,3->4仅适用于3号和4号。简言之,第1/2行的路径是4->3->1,而第3和第4行的路径是3->4->1,因为4和3都有同时改变的直系祖先。我们可以使用递归CTE进行此操作,但最后一行将使用无限递归。我为你准备了一个演示。啊,但这就是问题所在;我需要建立的东西,只有遵循儿童->家长的链接时,时间框架重叠。不幸的是,我的数据集充满了这样的对,其中两个条目在树中交换位置。谢谢你的帮助,我会坚持下去的。嗯。。。要么我遗漏了什么,要么提供的数据不完全正确。我创建了一个示例,但收到的记录较少。对于ID 5,我收到祖先1、2和3,但只有1次…5->1距离变化出现,因为3号的中间变化是4从4->3变为4->1。从5到1的路径从5->4->3->1更改为5->4->1。我很想从整个表中获取一个不同的开始和结束日期列表,让CTE在每个日期运行一次。
ID    Ancestor       Distance    StartDate     EndDate
------------------------------------------------------
 2           1              1    2000-01-01    2000-01-04 
 3           1              1    2000-01-01    2000-01-02
 3           4              1    2000-01-03    2000-01-04
 3           1              2    2000-01-03    2000-01-04
 4           3              1    2000-01-01    2000-01-02 
 4           1              2    2000-01-01    2000-01-02
 4           1              1    2000-01-03    2000-01-04
 5           4              1    2000-01-01    2000-01-02
 5           3              2    2000-01-01    2000-01-02
 5           1              3    2000-01-01    2000-01-02
 5           1              2    2000-01-03    2000-01-04
DECLARE @t TABLE (ID int, AncestorID int, StartDate date, EndDate date);
INSERT INTO @t VALUES (2, 1, '2000-01-01', '2000-01-04')
                     ,(3, 1, '2000-01-01', '2000-01-02')
                     ,(4, 3, '2000-01-01', '2000-01-02')
                     ,(5, 4, '2000-01-01', '2000-01-02')
                     ,(4, 1, '2000-01-03', '2000-01-04')
                     ,(3, 4, '2000-01-03', '2000-01-04');

DECLARE @MinAncestorID int = (SELECT min(AncestorID) FROM @t);

WITH cteBase AS(
  SELECT @MinAncestorID ID, 0 AncestorID, CONVERT(date, '1900-01-01') StartDate, CONVERT(date, '2999-12-31') EndDate
  UNION ALL
  SELECT t.ID, t.AncestorID, t.StartDate, t.EndDate
    FROM @t t
    JOIN cteBase c ON t.AncestorID = c.ID AND t.StartDate >= c.StartDate AND t.EndDate <= c.EndDate
),
cte2 AS(
SELECT ID, AncestorID, StartDate, EndDate
  FROM cteBase
  WHERE AncestorID > 0
UNION ALL
SELECT b.ID, a.AncestorID, a.StartDate, a.EndDate
  FROM cteBase AS b
  JOIN cte2 AS a ON a.ID = b.AncestorID AND a.StartDate >= b.StartDate AND a.EndDate <= b.EndDate
)
SELECT * FROM cte2
ORDER BY 1, 2
OPTION (MAXRECURSION 0)