如何写T-SQL Recursive Query to return hierarchical data using common self-joining table to store tree data

How to write T-SQL Recursive Query to return hierarchical data using common self-joining table to store tree data

每次我尝试重新访问递归查询时,我都觉得我正在重新开始。我想从 table 中查询数据,该 table 使用通用方法存储分层数据,具有自连接的 table。

首先,table 存储“组”,我认为它是使用 Windows Explorer 类比的“文件夹”。 ID就是PK,还有关联的组名。

CREATE TABLE [dbo].[BPAGroup](
    [id] [varchar](10) NOT NULL,
    [name] [nvarchar](255) NOT NULL,
 CONSTRAINT [PK_BPAGroup] PRIMARY KEY CLUSTERED 
(
    [id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

GO

其次也是最后一点,存在一种关系 table 将组与其父组相关联。 “GroupID”标识“MemberID”的组。例如,如果您将 BPAGroupGroup.MemberID 加入 [BPAGroup].ID,[BPAGroup].Name 将包含组的名称。如果你加入 BPAGroupGroup.GroupID 到 [BPAGroup].ID,[BPAGroup].Name 将包含 PARENT 组的名称。

CREATE TABLE [dbo].[BPAGroupGroup](
    [memberid] [varchar](10) NOT NULL,
    [groupid] [varchar](10) NOT NULL,
 CONSTRAINT [PK_BPAGroupGroup] PRIMARY KEY CLUSTERED 
(
    [memberid] ASC,
    [groupid] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

GO
-------------------------
ALTER TABLE [dbo].[BPAGroupGroup]  WITH CHECK ADD  CONSTRAINT [FK_BPAGroupGroup_BPAGroup_groupid] FOREIGN KEY([groupid])
REFERENCES [dbo].[BPAGroup] ([id])
GO

ALTER TABLE [dbo].[BPAGroupGroup] CHECK CONSTRAINT [FK_BPAGroupGroup_BPAGroup_groupid]
GO

ALTER TABLE [dbo].[BPAGroupGroup]  WITH CHECK ADD  CONSTRAINT [FK_BPAGroupGroup_BPAGroup_memberid] FOREIGN KEY([memberid])
REFERENCES [dbo].[BPAGroup] ([id])
GO

ALTER TABLE [dbo].[BPAGroupGroup] CHECK CONSTRAINT [FK_BPAGroupGroup_BPAGroup_memberid]
GO

下面是表单中的一些示例数据

Level1
   Level2
       Level3

和数据的SQL

INSERT [dbo].[BPAGroup] ([id], [name]) VALUES (N'A', N'Level1')
INSERT [dbo].[BPAGroup] ([id], [name]) VALUES (N'B', N'Level2')
INSERT [dbo].[BPAGroup] ([id], [name]) VALUES (N'C', N'Level3')
INSERT [dbo].[BPAGroupGroup] ([memberid], [groupid]) VALUES (N'B', N'A')
INSERT [dbo].[BPAGroupGroup] ([memberid], [groupid]) VALUES (N'C', N'B')

我如何编写递归 T-SQL 服务器查询 returns 所有组名称、递归“级别编号”和所有组的 ID。当然,树的根会有一个 NULL ParentID 和 ParentName?

例如,这些字段将在结果集中。

级别、GroupID、GroupName、ParentId、ParentName

我知道有多种方法可以存储这种类型的数据。我没有更改数据库设计的灵活性。

理想情况下,结果应显示所有没有父节点的组名,甚至是根节点。

根据最新数据,这似乎为您提供了您想要的结果:

WITH rCTE AS(
    SELECT 1 AS Level,
           id AS GroupID,
           [name] AS GroupName,
           CONVERT(nvarchar(10),NULL) AS ParentID, --This'll be uniqueidentifier in your real version
           CONVERT(nvarchar(255),NULL) AS ParentName
    FROM BPAGroup G
    WHERE NOT EXISTS (SELECT 1
                      FROM BPAGroupGroup e
                      WHERE e.memberid = G.id)
    UNION ALL
    SELECT r.Level + 1,
           G.id AS GroupID,
           G.[name] AS GroupName,
           r.GroupID AS ParentID,
           r.[GroupName] AS ParentName
    FROM BPAGroup G
         JOIN BPAGroupGroup GG ON G.id = GG.memberid
         JOIN rCTE r ON GG.groupid = r.GroupID)
SELECT *
FROM rCTE;

db<>fiddle

了解它的工作原理很重要。正如您在 post 中所说,您似乎每次都需要重新访问这些内容。需要检查某些东西的语法并没有错(有些东西我有时记不住,尤其是新的 OPENJSON 东西),但是你明白这是如何工作的吗?如果不是,你不喜欢哪一点?