1

我设法构建了一个递归查询,它返回所选 Id 及其所有子项的行。这对于最终的父母来说绝对没问题,但是当传递的 ID 是其中一个孩子的 ID 时,我也需要它正常工作,如果有的话,只显示孩子及其孩子。目前它仍然返回最终父级的其他子行加上传递的子行显示两次......

与上一期一样,我必须使用子查询格式来执行此操作,因为可能使用除 SQL Server 之外的其他基于 TSQL 的数据库引擎,它们不支持 CTE 或 WITH 子句。

期望的结果:

使用 Id 2,返回正确的数据:2、3、4、6、7。使用 Id 6,它应该只返回 6、7。目前查询返回 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
);

任何意见或建议都非常感激。

4

2 回答 2

2

所以...递归公用表表达式不仅仅是一个 using 的函数union all,它是在第二部分使用公用表表达式的自引用union all。人们不会通过尝试将其置于子查询/派生表来简单地在另一个 RDBMS 上复制此递归操作。

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

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

返回:

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

您现在拥有的查询将(在添加别名后)返回任何行@ProjectID,以及 3、4、6、7。因为您编写的内容将返回等于 的任何一行@ProjectID,以及所有具有父级(不是 0 或 -1)的行,它们是具有ProjectId3、4、6、7 的行。

rextester 演示@ProjectId = nullhttp ://rextester.com/VQU71307

于 2017-08-29T21:42:47.830 回答
0

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

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

/* 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 循环的结果创建一个中间表。此中间表可以按时间间隔更新,或者作为在主表中创建元素的一部分。这可能是您业务逻辑的一部分,也可能是数据库触发器。

如果您希望将此作为计划作业,这是我将编写的代码:

-- 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 更好。它同样适用于从下到上阅读,就像从上到下一样。

于 2017-08-29T21:52:04.407 回答