我有一个包含两列的表 - ChildPersonId和ParentPersonId。两个列中不能包含相同的ID。
CREATE TABLE Relationship
(PkId int IDENTITY(1,1) PRIMARY KEY, ChildPersonId int, GuardianPersonId int)
INSERT INTO Relationship (ChildPersonId, GuardianPersonId) VALUES
(42,24),(42,25),(42,56),(42,56),(43,24),(43,25),(43,26),(43,27),
(43,56),(44,29),(44,30),(45,31),(45,33),(46,34),(47,35),(48,36),
(48,37),(49,36),(49,37),(50,38),(50,39),(51,38),(51,39),(52,40),
(52,41),(53,40),(53,41),(57,24),(57,25),(57,26),(57,27),(57,56),
(63,24),(63,25),(63,26),(63,27),(63,56),(63,59),(64,59),(64,61),
(65,61),(65,62)
我想要一个查询,我可以根据数据中定义的关系传递ChildPersonId并返回所有相关的子项。因此,如果一个孩子有父母,我需要找到该父母的所有其他孩子,然后与那些孩子找到他们的父母,然后与那些父母一起找到孩子......你得到了递归图片。
ALMOST下面链接中的查询有效,但在性能方面有所下降:
Using recursive CTE to resolve a group, not hierarchy
以下示例实际确实返回了我想要的结果:
SELECT ChildPersonId FROM Relationship M5 INNER JOIN
(SELECT GuardianPersonId FROM Relationship M4 INNER JOIN
(SELECT ChildPersonId FROM Relationship M3 INNER JOIN
(SELECT GuardianPersonId FROM Relationship M2 INNER JOIN
(SELECT ChildPersonId FROM Relationship m1 INNER JOIN
(SELECT GuardianPersonId FROM Relationship
WHERE ChildPersonId = 42) g1
ON m1.GuardianPersonId = g1.GuardianPersonId) c1
ON m2.ChildPersonId = c1.ChildPersonId) g2
ON m3.GuardianPersonId = g2.GuardianPersonId) c2
ON m4.ChildPersonId = c2.ChildPersonId) g3
ON m5.GuardianPersonId = g3.GuardianPersonId
GROUP BY ChildPersonId
然而,这是一个丑陋的代码,我只会递归次数,因为我愿意削减&糊。
任何人都可以向我提示我需要关闭的递归CTE逻辑 - 没有上面示例中的WHERE子句破坏执行计划吗?
链接Getting all the children of a parent using MSSQL query显示另一种方法,但不返回ChildPersonIds 64和65 - 它似乎没有足够的递归。
非常感谢任何帮助 - 提前感谢。
答案 0 :(得分:0)
正如您所指出的,传统的CTE方法将匹配所有人ID 64和65.该方法很容易找到元素63.它找不到64和65,因为没有直接到达的递归路径。
例如,未达到64,因为唯一的链接是63到父59.但是,59没有链接到任何其他元素,因为它基本上是树中多个根节点之一。此外,未达到65,因为它的唯一路径是如上所述找到64,然后遍历其父61(也是根节点)到它的另一个孩子65.
传统的CTE递归不会找到那些备用路径,因此我已经包含了传统方法以及扩展,以便将父母兄弟姐妹作为关系路径进行迭代。由于数据结构无法退出递归条件,因此此扩展需要的不仅仅是CTE。
DECLARE @ChildPersonId INT = 42
; WITH cte AS (
SELECT
ChildPersonId,
GuardianPersonId
FROM Relationship R
WHERE ChildPersonId = @ChildPersonId
UNION ALL
SELECT
R.ChildPersonId,
R.GuardianPersonId
FROM cte
INNER JOIN Relationship R
ON cte.GuardianPersonId = R.ChildPersonId
)
SELECT DISTINCT
R.ChildPersonId
FROM cte
INNER JOIN Relationship R
ON cte.GuardianPersonId = R.GuardianPersonId
传统方法将产生
ChildPersonId
-------------
42
43
57
63
CREATE FUNCTION dbo.f_GetRelations(
@ChildPersonId INT
) RETURNS @Results TABLE (
ChildPersonId INT
)
AS
BEGIN
;WITH cte AS (
SELECT
ChildPersonId,
GuardianPersonId
FROM Relationship R
WHERE ChildPersonId = @ChildPersonId
UNION ALL
SELECT
R.ChildPersonId,
R.GuardianPersonId
FROM cte
INNER JOIN Relationship R
ON cte.GuardianPersonId = R.ChildPersonId
)
INSERT @Results
SELECT DISTINCT
R.ChildPersonId
FROM cte
INNER JOIN Relationship R
ON cte.GuardianPersonId = R.GuardianPersonId
DECLARE @Rows INT = -1
WHILE @Rows <> 0 BEGIN
INSERT @Results
SELECT DISTINCT
C.ChildPersonId
FROM @Results A
INNER JOIN Relationship B
ON A.ChildPersonId = B.ChildPersonId
INNER JOIN Relationship C
ON B.GuardianPersonId = C.GuardianPersonId
WHERE NOT EXISTS (SELECT 1 FROM @Results WHERE ChildPersonId = C.ChildPersonId)
SET @Rows = @@ROWCOUNT
END
RETURN
END
GO
SELECT A.ChildPersonId FROM f_GetRelations(42) A
此方法的输出产生与预期输出的匹配
ChildPersonId
-------------
42
43
57
63
64
65