给定以 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 的更好。此外,它同样适用于从下到上阅读和从上到下阅读。
我已经设法构建了一个递归查询,其中 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 的更好。此外,它同样适用于从下到上阅读和从上到下阅读。