给定以 child 开头的递归查询,我如何消除兄弟行和 parent 行?

Given a recursive query that starts with a child, how can I eliminate sibling and parent rows?

我已经设法构建了一个递归查询,其中 returns 行用于选定的 ID 及其所有 children。这对于最终的 parent 绝对有效,但我需要它在传递的 ID 是 children 之一时也能正常工作,仅显示 child 及其 child仁若有。目前它仍然 returns 最终 parent 的其他 child 行加上传递的 child 行显示两次...

与上一个问题一样,我必须使用 sub-query 格式来执行此操作,因为可能会使用不支持 CTE 的其他基于 TSQL 的数据库引擎而不是 SQL 服务器或 WITH 子句。

期望的结果:

使用 Id 2,正确的数据是 returned: 2, 3, 4, 6, 7。 使用 Id 6,它应该只有 return 6、7。当前查询 returns 6,3,4, 6,7.

数据:

ProjectId   ProjectName                             ParentId
1           Test Project                            -1
2           Test Project 2                          0
3           Test Project 2 Sub Project 1            2
4           Test Project 2 Sub Project 2            2
5           Test Project 3                          -1
6           Test Project 2 Sub Sub Project 1        3
7           Test Project 2 Sub Sub Sub Project 1    6

查询:

DECLARE @PROJECTID BIGINT = 2;

SELECT *
FROM            
(
    SELECT *
    FROM ProjectCostingProjects pcp
    WHERE pcp.[ProjectId] = @PROJECTID 
    UNION ALL
    SELECT  pcp2.*
    FROM    ProjectCostingProjects pcp2
    JOIN    ProjectCostingProjects pcp
    ON     pcp2.ParentID = pcp.ProjectId
);

非常感谢收到的任何意见或建议。

所以... recursive common table expression 不仅仅是使用 union all 的函数,它是在 union all。人们不会简单地通过尝试将其放入 subquery/derived table 来在另一个 RDBMS 上复制此递归操作。

如果要在 SQL 服务器中进行递归层次结构遍历,最好的选择是使用递归通用 table 表达式。

declare @projectid bigint = 6;

;with cte as (
  select *
  from ProjectCostingProjects pcp
  where pcp.[ProjectId] = @projectid 
  union all
  select  pcp2.*
  from    ProjectCostingProjects pcp2
    inner join cte 
      on pcp2.Parentid = cte.ProjectId
)
select *
from cte;

rextester 演示:http://rextester.com/XON59636

returns:

+-----------+--------------------------------------+----------+
| ProjectId |             ProjectName              | ParentId |
+-----------+--------------------------------------+----------+
|         6 | Test Project 2 Sub Sub Project 1     |        3 |
|         7 | Test Project 2 Sub Sub Sub Project 1 |        6 |
+-----------+--------------------------------------+----------+

您现在的查询将(添加别名后)return @ProjectID 所在的任何行,以及 3、4、6、7。因为您所写的内容将 return 等于 @ProjectID 的行,以及所有具有父项(不是 0 或 -1)的行,即 ProjectId 3 的行, 4、6、7。

使用 @ProjectId = null 的 rextester 演示:http://rextester.com/VQU71307

这是一个使用 while 循环的答案。

解决方案 1,使用 while 循环和临时 table

/* Populating the temp table with the data */
DECLARE @recurse TABLE
(projectId INT,parent int);

INSERT INTO @recurse (projectId,parent) VALUES (1,-1);
INSERT INTO @recurse (projectId,parent) VALUES (2,-0);
INSERT INTO @recurse (projectId,parent) VALUES (3,2);
INSERT INTO @recurse (projectId,parent) VALUES (4,2);
INSERT INTO @recurse (projectId,parent) VALUES (5,-1);
INSERT INTO @recurse (projectId,parent) VALUES (6,3);
INSERT INTO @recurse (projectId,parent) VALUES (7,6);

DECLARE @recurse2 TABLE
(projectId INT,parent INT);

--Start by inserting the root element
INSERT INTO @recurse2 ( projectId, parent)
SELECT * FROM @recurse WHERE projectId = 2

--insert elements until all children have all children
WHILE EXISTS (SELECT * FROM @recurse WHERE parent IN (SELECT projectId FROM @recurse2) AND projectId NOT IN (SELECT projectId FROM @recurse2) )
BEGIN
    INSERT INTO @recurse2
    (
        projectId,
        parent
    )
    SELECT * FROM @recurse WHERE parent IN (SELECT projectId FROM @recurse2) AND projectId NOT IN (SELECT projectId FROM @recurse2)
END 

SELECT * FROM @recurse2

解决方案 2 为了提高性能,您可以使用 while 循环的结果创建一个中间 table 。这个中间 table 可以间隔更新,或者作为在主 table 中创建元素的一部分。这可以是您业务逻辑的一部分,也可以作为数据库触发器。

如果您希望将此作为计划作业,我会编写以下代码:

-- Populating the temp table with the data 
DECLARE @recurse TABLE
(projectId INT,parent int);

INSERT INTO @recurse (projectId,parent) VALUES (1,-1);
INSERT INTO @recurse (projectId,parent) VALUES (2,-0);
INSERT INTO @recurse (projectId,parent) VALUES (3,2);
INSERT INTO @recurse (projectId,parent) VALUES (4,2);
INSERT INTO @recurse (projectId,parent) VALUES (5,-1);
INSERT INTO @recurse (projectId,parent) VALUES (6,3);
INSERT INTO @recurse (projectId,parent) VALUES (7,6);

DECLARE @recurse2 TABLE
(projectId INT,parent INT, lvl int);

--Start by inserting all elements root at lvl 0
INSERT INTO @recurse2 ( projectId, parent, lvl ) SELECT projectId, parent, 0 FROM @recurse 
SELECT * FROM @recurse2

--insert elements until we have all parents for all elements
WHILE EXISTS (SELECT * FROM @recurse2 a WHERE lvl IN (SELECT TOP 1 lvl FROM @recurse2 b WHERE a.projectId = b.projectId ORDER BY lvl DESC) AND a.parent > 0 )
BEGIN
    --Insert the next parent for all elements that does not have a their top level parent allready
    INSERT INTO @recurse2 ( projectId, parent , lvl )
    SELECT  projectId, 
            (SELECT b.parent FROM @recurse b WHERE b.projectId = a.parent), 
            lvl + 1 
    FROM @recurse2 a WHERE lvl IN (SELECT TOP 1 lvl FROM @recurse2 b WHERE a.projectId = b.projectId ORDER BY lvl DESC) AND a.parent > 0 
END 

--Find all children to an element
SELECT * FROM @recurse2 WHERE parent = 2

解决方案 #2 的最大优点是读取性能应该非常好,甚至可能比 CTE 的更好。此外,它同样适用于从下到上阅读和从上到下阅读。