SQL 从 table 查询父子完整路径

SQL Query Parent Child Full Path from table

我有一个 table 列出每个元素的父子关系,如下所示:

ParentID    ParentTitle ChildId ChildTitle
----------------------------------------------
  843       Documents   38737   Jobs    
  843       Documents   52537   Tools
  843       Documents    5763   SecondOps
  843       Documents    4651   Materials
38737       Jobs        16619   Job001
38737       Jobs        16620   Job002
38737       Jobs        16621   Job003
38737       Jobs        16622   Job004
38737       Jobs        16623   Job005
52537       Tools        1952   HandTools
52537       Tools        1953   Automated
52537       Tools        1957   Custom
 1952       HandTools      12   Cordless10mm
 1952       HandTools      13   Cordless8mm
 1952       HandTools      14   CableCrimp
 1952       HandTools      15   Cutter
 1952       HandTools      16   EdgePlane
 5763       SecondOps     101   Procedure001
 5763       SecondOps     102   Procedure002
 5763       SecondOps     103   Procedure003
 4651       Materials   33576   Raw
 4651       Materials   33577   Mixed
 4651       Materials   33578   Hybrid
 4651       Materials   33579   Custom
16622       Job004        101   Procedure001
16622       Job004         14   CableCrimp
16622       Job004         15   Cutter
16622       Job004       4651   Mixed
16623       Job005        102   Procedure002
16623       Job005        103   Procedure003
16623       Job005      16619   Job001
16623       Job005       1953   Automated
16623       Job005      33579   Custom
16623       Job005      33576   Raw

我想使用 ID 获取每个组合的完整路径,例如

Documents\Jobs\Job003 = 843737621

另一个例子是 "Procedure001",它在 2 个地方列出

Documents\SecondOps\Procedure001 = 843631

这里也引用了相同的文档:

Documents\Jobs\Job004\Procedure001 = 8437376221

我想利用这个 table 并在 .NET 中构建一个 TreeView。因此,拥有每个项目的完整路径将使它变得轻而易举。

否则,我在想我可以从根页面开始,不断递归父级,构建子列表,然后递归那些,等等。

是否有更好的查询方法来构建这些路径?这个列表有 400,000 条记录,所以如果有更有效的方法可以节省时间

这一切最初都在 AS400 系统 DB 中,直到 2000 年左右才成为 MediaWiki 站点。我正在通过 api 提取数据,目的是为 SQL 服务器数据库构建一个接口。

我可以进行基本的 SQL 查询、连接、联合等

如果不清楚,请告诉我我可以提供哪些其他信息

如果您使用 SQL SERVER MS,您可以使用 INNER JOINLEFT JOIN,下面是查询的样子,它将为您提供完整的结果(组合)根据您的要求:

SELECT A.ParentTitle + '\'+B.ParentTitle+ 

                                         CASE WHEN C.ParentTitle IS NOT NULL THEN '\' +C.ParentTitle
                                         ELSE ''
                                         END
     +
     ' =' + A.ParentID + '\'+B.ParentID+ 

                                         CASE WHEN C.ParentID IS NOT NULL THEN '\' +C.ParentID
                                         ELSE ''
                                         END


FROM TABLE AS A 
INNER JOIN TABLE AS B
ON B.ParentID = A.ChildId
LEFT JOIN TABLE AS C
ON C.ParentID = B.ChildId

不能 100% 确定它是否会像我预期的那样工作,请试一试 xD

树结构意味着通用解决方案的递归。 请不要在 sql 中尝试此操作。只需将 sql 中的数据行放入列表或类似的东西中,然后使用编程语言进行递归填充。

你的树 class 会像:

public class MyObj {
    public int Id {get; set;}
    public string Title {get; set;}
    public List<MyObj> {get; set; } = null;
}

0.You table 大错特错。正确的方法是:

 CREATE TABLE Jobs(
   Id int not null primary key,
   Title nvarchar(255) not null,
   StartTime datetime,--optional maybe will help
   ParentId int null --can be null root will have no parent

   )

但我会尝试在您的 table 上解释它是如何完成的。 我假设您有某种数据上下文(DBML、EDMX 等)

  1. 求根或根。在您的情况下,root 将是那些在 ParentID 上但不在 ChildId 上的 nr。

将列出您的根的查询:

SELECT DISTINCT a.ParentId FROM
YourTable a LEFT JOIN 
YourTable b ON a.ParentId=b.ChildId
WHERE b.ParentId is null 
  1. 制作一个递归过程,将在 class 结构中检索您的数据,如上(MyObj)。

    程序 MyObj GetTree(int id, db){ 如果 (db.YourTable.Any(r => r.ParentId==Id)){

          var q=db.YourTable.Select(r => r.ParentId==Id).ToList();
          var result = new MyObj{
              Id = q.ParentId,
              Title = q.ParentTitle,
              Children = new List<MyObj>()   
          }
          foreach( var el in q) {
              if (db.YourTable.Any(r => r.ParentId==el.ChildId))
              result.Children.Add(GetTree(el.ChildId,db))
              else 
              result.Children.Add( new MyObj{
               Id = el.ChildId,
              Title = el.ChildTitle,
              Children = null 
               });
              return result;
          }
    
     }
     return null;
    

    }

  2. 使用存储在列表中的第 1 点的列表 ID 创建树 假设 ListIds 你会做类似的事情:

    List finaltrees = new List()</p> <p>Ids.ForEach(id => finaltrees.Add(GetTree(id,dbcontext));

现在你在 finaltrees 中有了一个树结构。

PS:

我直接在浏览器(C#)中写代码,可能会有一些拼写错误。

所以为了详细说明我正在尝试做的事情,我正在使用不使用命名空间来建立文档路径的 wiki 版本。

例如,如果页面在这样的文档树中有 3 层深度

  • 根页面
    • 第01页
      • 第02页
        • 第03页
        • 第04页

使用命名空间方法 Page03 的名称(路径)是 "RootPage:Page01:Page02:Page03"

我想对 PageID 做同样的事情

所以给定这个例子你会

  • PageTitle PageId 路径
  • 根页面 001 001
  • Page01 101 001:101
  • Page02 201 001:101:201
  • Page03 301 001:101:201:301
  • Page04 302 001:101:201:302

所以现在我要做的就是将 PagePath 放在一起。

这个 wiki 有几个挑战需要考虑

  1. 不能有 2 个文档具有相同的 TITLE
  2. 文档ID基本上是 无关紧要,但在这种情况下很方便(至少在我的版本中 正在处理)
  3. 谢天谢地,有一个页面列表及其 "Links" 或 子页面。我相信你会称它为 MANY to MANY

要记住的 要点 是,即使一个页面被列为许多其他页面的子页面,实际上也只有一个页面存在,我只需要其中一个出现在结果中。

所以使用 这里是我到达的地方

使用这个Table:

CREATE Table [dbo].[ExampleTable](
[RecordID] Int IDENTITY (1, 1) Not NULL,
[ParentID] Int Not NULL,
[ParentTitle] VARCHAR(800) NULL,
[ChildID] Int Not NULL,
[ChildTitle] VARCHAR(800) NULL,
PRIMARY KEY CLUSTERED ([RecordID] ASC),);

这个数据:

INSERT INTO [dbo].[ExampleTable]
([ParentID]
,[ParentTitle]
,[ChildID]
,[ChildTitle])
VALUES
(843,'Documents',38737,'Jobs'),
(843,'Documents',52537,'Tools'),
(843,'Documents',5763,'SecondOps'),
(843,'Documents',4651,'Materials'),
(38737,'Jobs',16619,'Job001'),
(38737,'Jobs',16620,'Job002'),
(38737,'Jobs',16621,'Job003'),
(38737,'Jobs',16622,'Job004'),
(38737,'Jobs',16623,'Job005'),
(52537,'Tools',1952,'HandTools'),
(52537,'Tools',1953,'Automated'),
(52537,'Tools',1957,'Custom'),
(1952,'HandTools',12,'Cordless10mm'),
(1952,'HandTools',13,'Cordless8mm'),
(1952,'HandTools',14,'CableCrimp'),
(1952,'HandTools',15,'Cutter'),
(1952,'HandTools',16,'EdgePlane'),
(5763,'SecondOps',101,'Procedure001'),
(5763,'SecondOps',102,'Procedure002'),
(5763,'SecondOps',103,'Procedure003'),
(4651,'Materials',33576,'Raw'),
(4651,'Materials',33577,'Mixed'),
(4651,'Materials',33578,'Hybrid'),
(4651,'Materials',33579,'Custom'),
(16622,'Job004',101,'Procedure001'),
(16622,'Job004',14,'CableCrimp'),
(16622,'Job004',15,'Cutter'),
(16622,'Job004',4651,'Mixed'),
(16623,'Job005',102,'Procedure002'),
(16623,'Job005',103,'Procedure003'),
(16623,'Job005',16619,'Job001'),
(16623,'Job005',1953,'Automated'),
(16623,'Job005',33579,'Custom'),
(16623,'Job005',33576,'Raw')
GO

还有这个查询,我从 LONG 的例子中修改而来:

SELECT DISTINCT C.ChildTitle as PageTitle, convert(varchar(20),A.ParentID) + ':' + convert(varchar(20),B.ParentID) + 

CASE WHEN C.ParentID IS NOT NULL THEN ':' + convert(varchar(20),C.ParentID)
ELSE ''
END

+

CASE WHEN C.ChildID IS NOT NULL THEN ':' + convert(varchar(20),C.ChildID)
ELSE ''
END

FROM ExampleTable AS A 
INNER JOIN ExampleTable AS B
ON B.ParentID = A.ChildId
LEFT JOIN ExampleTable AS C
ON C.ParentID = B.ChildId
ORDER By PageTitle

我得到这些结果:

PageTitle           UnNamed
NULL        16622:4651
NULL        38737:16622
NULL        38737:16623
NULL        52537:1952
NULL        843:38737
NULL        843:4651
NULL        843:52537
NULL        843:5763
Automated   843:38737:16623:1953
CableCrimp  843:38737:16622:14
CableCrimp  843:52537:1952:14
Cordless10mm    843:52537:1952:12
Cordless8mm 843:52537:1952:13
Custom      38737:16622:4651:33579
Custom      843:38737:16623:33579
Cutter      843:38737:16622:15
Cutter      843:52537:1952:15
EdgePlane   843:52537:1952:16
Hybrid      38737:16622:4651:33578
Job001      843:38737:16623:16619
Mixed       38737:16622:4651:33577
Mixed       843:38737:16622:4651
Procedure001    843:38737:16622:101
Procedure002    843:38737:16623:102
Procedure003    843:38737:16623:103
Raw     38737:16622:4651:33576
Raw     843:38737:16623:33576

我想得到的是每个页面的单次出现,不管它恰好是哪个父页面

然后我可以使用这些路径将虚拟树结构变成实际的树结构。

最后一个问题是实际的 Link 列表与我创建的示例非常相似,只是它有 400,000 条记录。

当我 运行 这个查询针对实际 "Link List" 它 运行s 大约 17 分钟并且 运行s 内存不足 .

我一直在研究 MAXRECURSION 选项,但我仍在研究它,不知道这是否有问题。