具有父子关系的递归查询
Recursive query with parent-child relation
我正在尝试在 SQL 服务器中进行递归查询,以分层显示数据。这是 table
的结构
[id] [int] IDENTITY(1,1) NOT NULL,
[name] [varchar(100)] NOT NULL,
[Parent_Id] [int] NULL,
每个产品都有父项。 Parent_Id 列包含父级的 ID。 parent_id 对于根产品为空。
我想创建一个 sql 查询来分层显示产品。下图是如何组织产品的示例。
产品可以有子产品。
对于上图,查询结果应该是这样的:
id name parent_id
1 P1 NULL
2 P2 NULL
3 P2-1 2
4 P2-2 2
5 P2-3 2
6 P2-3-1 5
7 P2-3-2 5
8 P3 NULL
9 P3-1 8
这是我为实现它而写的请求:
with tree as (select * from products
union all
select * from tree where parent_id = tree.id
)
select * from tree;
但我得到的结果类似于以下内容:
1 P1 NULL
2 P2 NULL
8 P3 NULL
3 P2-1 2
4 P2-2 2
5 P2-3 2
9 P3-1 8
6 P2-3-1 5
7 P2-3-2 5
我想要的是对每个兄弟产品进行分组,以便每个产品都显示在其直接父项下。
构造递归查询中的路径。以下将其作为具有固定长度 ID 的字符串执行此操作:
with tree as (
select p.id, p.name, p.parentid,
format(p.parentid, '0000') as path
from products p
where p.parentid is null
union all
select p.id, p.name, p.parentid,
concat(cte.path, '->', format(p.id, '0000')
from tree join
products p
where p.parent_id = t.id
)
select *
from tree;
如果我的理解是正确的,并且您得到了您想要的结果但未排序,您应该能够按名称对结果进行排序。
with tree as (select * from products
union all
select * from tree where parent_id = tree.id
)
select * from tree order by name asc;
只是使用数据类型 hierarchyid
的另一种选择
还有一些与 hierarchyid
相关的附加特性和功能
例子
-- Optional See 1st WHERE
Declare @Top int = null --<< Sets top of Hier Try 2
;with cteP as (
Select ID
,parent_id
,Name
,HierID = convert(hierarchyid,concat('/',ID,'/'))
From YourTable
Where IsNull(@Top,-1) = case when @Top is null then isnull(parent_id ,-1) else ID end
--Where parent_id is null -- Use this where if you always want the full hierarchy
Union All
Select ID = r.ID
,parent_id = r.parent_id
,Name = r.Name
,HierID = convert(hierarchyid,concat(p.HierID.ToString(),r.ID,'/'))
From YourTable r
Join cteP p on r.parent_id = p.ID)
Select Lvl = HierID.GetLevel()
,ID
,parent_id
,Name
From cteP A
Order By A.HierID
结果
Lvl ID parent_id Name
1 1 NULL P1
1 2 NULL P2
2 3 2 P2-1
2 4 2 P2-2
2 5 2 P2-3
3 6 5 P2-3-1
3 7 5 P2-3-2
1 8 NULL P3
2 9 8 P3-1
只是为了好玩,如果我将 @Top
设置为 2,结果将是
Lvl ID parent_id Name
1 2 NULL P2
2 3 2 P2-1
2 4 2 P2-2
2 5 2 P2-3
3 6 5 P2-3-1
3 7 5 P2-3-2
我正在尝试在 SQL 服务器中进行递归查询,以分层显示数据。这是 table
的结构 [id] [int] IDENTITY(1,1) NOT NULL,
[name] [varchar(100)] NOT NULL,
[Parent_Id] [int] NULL,
每个产品都有父项。 Parent_Id 列包含父级的 ID。 parent_id 对于根产品为空。
我想创建一个 sql 查询来分层显示产品。下图是如何组织产品的示例。
产品可以有子产品。
对于上图,查询结果应该是这样的:
id name parent_id
1 P1 NULL
2 P2 NULL
3 P2-1 2
4 P2-2 2
5 P2-3 2
6 P2-3-1 5
7 P2-3-2 5
8 P3 NULL
9 P3-1 8
这是我为实现它而写的请求:
with tree as (select * from products
union all
select * from tree where parent_id = tree.id
)
select * from tree;
但我得到的结果类似于以下内容:
1 P1 NULL
2 P2 NULL
8 P3 NULL
3 P2-1 2
4 P2-2 2
5 P2-3 2
9 P3-1 8
6 P2-3-1 5
7 P2-3-2 5
我想要的是对每个兄弟产品进行分组,以便每个产品都显示在其直接父项下。
构造递归查询中的路径。以下将其作为具有固定长度 ID 的字符串执行此操作:
with tree as (
select p.id, p.name, p.parentid,
format(p.parentid, '0000') as path
from products p
where p.parentid is null
union all
select p.id, p.name, p.parentid,
concat(cte.path, '->', format(p.id, '0000')
from tree join
products p
where p.parent_id = t.id
)
select *
from tree;
如果我的理解是正确的,并且您得到了您想要的结果但未排序,您应该能够按名称对结果进行排序。
with tree as (select * from products
union all
select * from tree where parent_id = tree.id
)
select * from tree order by name asc;
只是使用数据类型 hierarchyid
还有一些与 hierarchyid
相关的附加特性和功能例子
-- Optional See 1st WHERE
Declare @Top int = null --<< Sets top of Hier Try 2
;with cteP as (
Select ID
,parent_id
,Name
,HierID = convert(hierarchyid,concat('/',ID,'/'))
From YourTable
Where IsNull(@Top,-1) = case when @Top is null then isnull(parent_id ,-1) else ID end
--Where parent_id is null -- Use this where if you always want the full hierarchy
Union All
Select ID = r.ID
,parent_id = r.parent_id
,Name = r.Name
,HierID = convert(hierarchyid,concat(p.HierID.ToString(),r.ID,'/'))
From YourTable r
Join cteP p on r.parent_id = p.ID)
Select Lvl = HierID.GetLevel()
,ID
,parent_id
,Name
From cteP A
Order By A.HierID
结果
Lvl ID parent_id Name
1 1 NULL P1
1 2 NULL P2
2 3 2 P2-1
2 4 2 P2-2
2 5 2 P2-3
3 6 5 P2-3-1
3 7 5 P2-3-2
1 8 NULL P3
2 9 8 P3-1
只是为了好玩,如果我将 @Top
设置为 2,结果将是
Lvl ID parent_id Name
1 2 NULL P2
2 3 2 P2-1
2 4 2 P2-2
2 5 2 P2-3
3 6 5 P2-3-1
3 7 5 P2-3-2