时间:2016-12-27 13:32:09

标签: sql-server recursion path parent-child jointable

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

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 = 843\38737\16621

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

Documents\SecondOps\Procedure001 = 843\5763\101

这里也引用了同一文件:

Documents\Jobs\Job004\Procedure001 = 843\38737\16622\101

我想使用此表并在.NET中构建TreeView。因此,拥有每个项目的完整路径将使它成为一个蛋糕步行。

否则,我想我可以从Root页面开始,继续通过父级递归,构建子列表,然后递归这些,等等。

有没有更好的方法来查询它来构建这些路径?此列表有400,000条记录,因此如果有更有效的方式可以节省时间

这一切都是在AS400系统数据库中直到2000年,然后被制作成MediaWiki网站。我正在通过api提取数据,目的是为SQL Server数据库构建一个接口。

我可以做基本的SQL查询,连接,工会等。

如果不清楚,我可以告诉我其他信息

3 个答案:

答案 0 :(得分:1)

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

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

答案 1 :(得分:1)

树结构意味着通用解决方案的递归。 请不要在sql中尝试这个。只需将sql中的datarow转换为列表或其他类似的东西,然后用编程语言填充递归。

你的树类会像:

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

0.你表示非常错误。核心方式是:

 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

   )

但我会尝试在你的桌子上解释它是如何完成的。 我想你有一些类型的datacontext(DBML,EDMX等)

  1. 查找根或根。在你的情况下,root将是那些在ParentID上并且不在ChildId上的nr。
  2. 将列出您的根的查询:

    SELECT DISTINCT a.ParentId FROM
    YourTable a LEFT JOIN 
    YourTable b ON a.ParentId=b.ChildId
    WHERE b.ParentId is null 
    
    1. 创建一个递归过程,在上面的类结构中重新检索数据(MyObj)。

      过程MyObj GetTree(int id,db){      if(db.YourTable.Any(r =&gt; 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. 使用列表中存储的列表ID生成树,让我们说ListIds你会做类似的事情:

      List finaltrees = new List()

      Ids.ForEach(id => finaltrees.Add(GetTree(id,dbcontext));

    3. 现在你在最终树中有了一个树形结构。

      PS:

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

答案 2 :(得分:0)

因此,要详细说明我要做的事情,我正在使用一个不使用命名空间来建立文档路径的维基版本。

例如,如果页面在文档树上有3层深度,如此

  • RootPage
    • 页01
      • Page02
        • Page03
        • Page04

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

我想用PageIDs

做同样的事情

所以给出这个例子你会有

  • PageTitle PageId Path
  • RootPage 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个文件可以有相同的标题
  2. 文档ID基本上是 不相关,但在这种情况下很方便(至少在我的版本中 工作)
  3. 值得庆幸的是,有一个页面列表及其&#34;链接&#34;要么 子页面。我相信你会把它称为很多很多
  4. 要记住的关键点即使页面被列为许多其他页面的子项,也只有一个页面存在,我只需要其中一个页面。

    所以在这里使用LONG's example就是我要去的地方

    使用此表:

    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
    

    我想得到的是每个页面的单一出现,不管找到哪个家长

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

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

    当我针对实际的&#34;链接列表&#34;运行此查询时它运行大约17分钟,内存不足

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