Sql 以逗号分隔的字符串显示所有父项,直到最顶端
我有一个SQL server表,我们使用SQL server 2017,Org_Relationship,它只有两列OrgId和ParentOrgId。ParentOrgId列保存任何给定组织的父组织id值,只要该值可用。我需要以逗号分隔的连接字符串显示所有组织的层次结构,从直接父级开始,一直到最终父级 有许多关于堆栈溢出的类似文章讨论了类似的主题,但我无法找到符合我需要的解决方案。我可能忽略了已经为类似问题提供的解决方案,因为我是SQL Server新手 下面给出了创建脚本和一些示例数据:Sql 以逗号分隔的字符串显示所有父项,直到最顶端,sql,sql-server,Sql,Sql Server,我有一个SQL server表,我们使用SQL server 2017,Org_Relationship,它只有两列OrgId和ParentOrgId。ParentOrgId列保存任何给定组织的父组织id值,只要该值可用。我需要以逗号分隔的连接字符串显示所有组织的层次结构,从直接父级开始,一直到最终父级 有许多关于堆栈溢出的类似文章讨论了类似的主题,但我无法找到符合我需要的解决方案。我可能忽略了已经为类似问题提供的解决方案,因为我是SQL Server新手 下面给出了创建脚本和一些示例数据: C
CREATE TABLE [dbo].[OrgHiearchy](
[OrgId] [nvarchar](50) NOT NULL,
[ParentOrgID] [nvarchar](50) NULL
)
组织ID 100和200的预期输出为:
非常感谢您的帮助。有一个解决方案可以满足您的需要。我鼓起勇气将其应用于MS SQL Server
输出:
si_id si_item_fullname
1 Paper
5 Paper->Non-Recycled
6 Paper->Non-Recycled->20 lb
7 Paper->Non-Recycled->40 lb
8 Paper->Non-Recycled->Scraps
2 Paper->Recycled
3 Paper->Recycled->20 lb
4 Paper->Recycled->40 lb
首先,我们将构建层次结构。为此,我们将创建一个名为Hierarchys的递归CTE CTE的锚定部分将选择层次结构的起点。我们可以在CTE中创建包含此锚查询的基本查询,如下所示:
WITH
[Hierarchies] ([BaseId], [Level], [Id]) AS
(
SELECT [OrgId], 1, [ParentOrgId]
FROM [OrgHierarchy]
)
SELECT *
FROM [Hierarchies]
ORDER BY [BaseId], [Level];
WITH
[Hierarchies] ([BaseId], [Level], [Id]) AS
(
SELECT [OrgId], 1, [ParentOrgId]
FROM [OrgHierarchy]
UNION ALL
SELECT [BaseId], [Level] + 1, [ParentOrgId]
FROM [OrgHierarchy] INNER JOIN [Hierarchies] ON [Id] = [OrgId]
)
SELECT *
FROM [Hierarchies]
ORDER BY [BaseId], [Level];
它提供以下输出:
BaseId Level Id
100 1 600011944
200 1 1045
1045 1 250013
250013 1 600021987
600011944 1 600011945
BaseId Level Id
100 1 600011944
100 2 600011945
200 1 1045
200 2 250013
200 3 600021987
1045 1 250013
1045 2 600021987
250013 1 600021987
600011944 1 600011945
BaseId Level Id Hierarchy
100 1 600011944 600011944
100 2 600011945 600011944, 600011945
200 1 1045 1045
200 2 250013 1045, 250013
200 3 600021987 1045, 250013, 600021987
1045 1 250013 250013
1045 2 600021987 250013, 600021987
250013 1 600021987 600021987
600011944 1 600011945 600011945
OrgId ParentOrgId Hierarchy
100 600011944 600011944, 600011945
200 1045 1045, 250013, 600021987
1045 250013 250013, 600021987
250013 600021987 600021987
600011944 600011945 600011945
BaseId字段将始终包含起点的Id。对于层次结构中的每个新级别,该BaseId字段都将被复制。
当然,每个步骤的级别字段都会增加。
Id字段将包含该特定级别的父Id
因此,我们可以通过添加递归查询来扩展CTE,该查询使用UNION ALL运算符与锚查询分离,如下所示:
WITH
[Hierarchies] ([BaseId], [Level], [Id]) AS
(
SELECT [OrgId], 1, [ParentOrgId]
FROM [OrgHierarchy]
)
SELECT *
FROM [Hierarchies]
ORDER BY [BaseId], [Level];
WITH
[Hierarchies] ([BaseId], [Level], [Id]) AS
(
SELECT [OrgId], 1, [ParentOrgId]
FROM [OrgHierarchy]
UNION ALL
SELECT [BaseId], [Level] + 1, [ParentOrgId]
FROM [OrgHierarchy] INNER JOIN [Hierarchies] ON [Id] = [OrgId]
)
SELECT *
FROM [Hierarchies]
ORDER BY [BaseId], [Level];
它提供以下输出:
BaseId Level Id
100 1 600011944
200 1 1045
1045 1 250013
250013 1 600021987
600011944 1 600011945
BaseId Level Id
100 1 600011944
100 2 600011945
200 1 1045
200 2 250013
200 3 600021987
1045 1 250013
1045 2 600021987
250013 1 600021987
600011944 1 600011945
BaseId Level Id Hierarchy
100 1 600011944 600011944
100 2 600011945 600011944, 600011945
200 1 1045 1045
200 2 250013 1045, 250013
200 3 600021987 1045, 250013, 600021987
1045 1 250013 250013
1045 2 600021987 250013, 600021987
250013 1 600021987 600021987
600011944 1 600011945 600011945
OrgId ParentOrgId Hierarchy
100 600011944 600011944, 600011945
200 1045 1045, 250013, 600021987
1045 250013 250013, 600021987
250013 600021987 600021987
600011944 600011945 600011945
这一数据结构良好。
现在,我们将更新CTE下面的主查询,以将父ID分组为逗号分隔的值。
为此,我们可以使用FOR-XML查询,但是因为我们已经递归地遍历了层次结构,所以我们还可以在CTE中构建csv
让我们在CTE中包含一个名为Hierarchy的附加字段,它是一个长varchar类型,可以保存所有以逗号分隔的父ID。
锚查询只会将第一个父id放入其中。递归查询将添加逗号和新的父id
WITH
[Hierarchies] ([BaseId], [Level], [Id], [Hierarchy]) AS
(
SELECT [OrgId], 1, [ParentOrgId], CAST([ParentOrgId] AS VARCHAR(MAX))
FROM [OrgHierarchy]
UNION ALL
SELECT [BaseId], [Level] + 1, [ParentOrgId], [Hierarchy] + ', ' + CAST([ParentOrgId] AS VARCHAR(MAX))
FROM [OrgHierarchy] INNER JOIN [Hierarchies] ON [Id] = [OrgId]
)
SELECT *
FROM [Hierarchies]
ORDER BY [BaseId], [Level];
它提供以下输出:
BaseId Level Id
100 1 600011944
200 1 1045
1045 1 250013
250013 1 600021987
600011944 1 600011945
BaseId Level Id
100 1 600011944
100 2 600011945
200 1 1045
200 2 250013
200 3 600021987
1045 1 250013
1045 2 600021987
250013 1 600021987
600011944 1 600011945
BaseId Level Id Hierarchy
100 1 600011944 600011944
100 2 600011945 600011944, 600011945
200 1 1045 1045
200 2 250013 1045, 250013
200 3 600021987 1045, 250013, 600021987
1045 1 250013 250013
1045 2 600021987 250013, 600021987
250013 1 600021987 600021987
600011944 1 600011945 600011945
OrgId ParentOrgId Hierarchy
100 600011944 600011944, 600011945
200 1045 1045, 250013, 600021987
1045 250013 250013, 600021987
250013 600021987 600021987
600011944 600011945 600011945
看来我们快到了。我们只需要选择所有最终记录。
为此,我们在主查询中使用交叉应用程序,将OrgHierarchy表中的原始字段与最后一个对应的CTE记录组合在一起
WITH
[Hierarchies] ([BaseId], [Level], [Id], [Hierarchy]) AS
(
SELECT [OrgId], 1, [ParentOrgId], CAST([ParentOrgId] AS VARCHAR(MAX))
FROM [OrgHierarchy]
UNION ALL
SELECT [BaseId], [Level] + 1, [ParentOrgId], [Hierarchy] + ', ' + CAST([ParentOrgId] AS VARCHAR(MAX))
FROM [OrgHierarchy] INNER JOIN [Hierarchies] ON [Id] = [OrgId]
)
SELECT [OrgId], [ParentOrgId], [Hierarchy]
FROM
[OrgHierarchy]
CROSS APPLY (SELECT TOP (1) [Hierarchy]
FROM [Hierarchies]
WHERE [BaseId] = [OrgId]
ORDER BY [Level] DESC) AS H
它提供以下输出:
BaseId Level Id
100 1 600011944
200 1 1045
1045 1 250013
250013 1 600021987
600011944 1 600011945
BaseId Level Id
100 1 600011944
100 2 600011945
200 1 1045
200 2 250013
200 3 600021987
1045 1 250013
1045 2 600021987
250013 1 600021987
600011944 1 600011945
BaseId Level Id Hierarchy
100 1 600011944 600011944
100 2 600011945 600011944, 600011945
200 1 1045 1045
200 2 250013 1045, 250013
200 3 600021987 1045, 250013, 600021987
1045 1 250013 250013
1045 2 600021987 250013, 600021987
250013 1 600021987 600021987
600011944 1 600011945 600011945
OrgId ParentOrgId Hierarchy
100 600011944 600011944, 600011945
200 1045 1045, 250013, 600021987
1045 250013 250013, 600021987
250013 600021987 600021987
600011944 600011945 600011945
看来我们现在结束了 递归CTE可能很棘手,但这正是您想要的:
with cte as (
select oh.OrgId, oh.ParentOrgId,
convert(varchar(max), oh.ParentOrgId) as parents,
oh.ParentOrgId as working, 1 as lev
from OrgHierarchy oh
where not exists (select 1 from OrgHierarchy oh2 where oh2.ParentOrgId = oh.OrgId)
union all
select cte.OrgId, cte.ParentOrgId,
concat(parents, ',', convert(varchar(max), oh.ParentOrgId)),
oh.ParentOrgId, lev + 1
from cte join
OrgHierarchy oh
on oh.OrgId = cte.working
where lev < 5
)
select top (1) with ties orgid, parentorgid, parents
from cte
order by row_number() over (partition by orgid order by lev desc);
是一把小提琴。嗨,欢迎来到SO。要解决这个问题,您需要一个递归cte来获取所有数据。然后,您需要使用FOR XML生成带分隔符的列表。我得走了,但几个小时后我会回来看看你是不是找到了答案,还是有人给你发了答案。谢谢你的回复肖恩。我确实遇到过使用STUFF和FOR XML on stack overflow的CTE解决方案,但由于我是SQL server新手,所以无法自己实现。我会尽力去做的,不要一步到位。这些都是高级主题。首先让cte工作以获取数据。然后创建分隔字符串。嗨,肖恩。是的,我一次尝试了所有的概念,可能这就是错误。谢谢你给我指出了正确的方向。非常感谢你的回答。当我执行查询时,我得到了下面的错误。CONCAT不是一个可识别的内置函数名。它可能与我假设的组织中安装的SQL server版本有关。但再次感谢你@罗希特。你可以用+操作符替换concat。哇!非常感谢您对解决方案查询的彻底分解。我敢肯定,关于最终查询中使用的所有结构,还有很多需要了解的内容,而您所解释的方式确实让我想了解它们。谢谢你!我所要做的就是按原样运行查询,它给了我预期的输出。非常感谢!嘿,巴特,一个简短的问题。如果我们要在上面的输出中也显示OrgId,您能告诉我在上面的查询中需要修改什么吗。在层次结构列中,上述情况下OrgID 100的预期输出为100600011944、600011945。如有任何建议,将不胜感激。谢谢。我成功地使用了这种语法-Orgid+','+[Hierarchy]。非常感谢您为基于Postgre的SQL server编写了一个查询。虽然我已经标记了一个不同的解决方案作为这个特定问题的答案,但我相信我将能够利用您的建议来解决类似的问题。再次感谢您!