Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/sql-server/26.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Sql server 使用递归CTE对父列表中的所有子项进行分组_Sql Server_Hierarchy_Common Table Expression - Fatal编程技术网

Sql server 使用递归CTE对父列表中的所有子项进行分组

Sql server 使用递归CTE对父列表中的所有子项进行分组,sql-server,hierarchy,common-table-expression,Sql Server,Hierarchy,Common Table Expression,我有一个有两列的表——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),(4

我有一个有两列的表——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,并根据数据中定义的关系返回所有相关的子级。因此,如果一个孩子有父母,我需要找到该父母的所有其他孩子,然后与这些孩子一起找到他们的父母,然后与这些父母一起,找到孩子……你得到了递归的图片

下面链接中的查询几乎可以正常工作,但在性能方面远远不够:

下面的示例实际返回了我想要的结果:

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子句,它破坏了执行计划

该链接显示了另一个方法,但没有返回ChildPersonId 64和65-它似乎没有足够的递归


任何帮助都将不胜感激-提前感谢。

如您所述,传统的CTE方法将匹配除个人ID 64和65之外的所有人。该方法很容易找到元素63。它找不到64和65,因为没有直接到达的递归路径

例如,无法访问64,因为唯一的链接是通过父级59连接到63。但是,59没有链接到任何其他元素,因为它基本上是树中多个根节点之一。此外,未到达65,因为它的唯一路径是如上所述查找64,然后遍历其父节点61以及根节点到它的另一个子节点65

传统的CTE递归将无法找到这些备用路径,因此我包括了一个传统方法以及一个扩展,该扩展还可以将父兄弟姐妹迭代为关系路径。由于数据结构无法退出递归条件,此扩展需要的不仅仅是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