Полный путь родительского дочернего элемента SQL-запроса из таблицы

У меня есть таблица, в которой перечислены родительские дочерние отношения для каждого элемента, например:

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

Я хотел бы получить полный путь каждой комбинации, используя идентификаторы, например

Documents\Jobs\Job003 = 843\38737\16621

Другим примером может быть «Procedure001», который указан в 2 местах.

Documents\SecondOps\Procedure001 = 843\5763\101

Этот же документ также упоминается здесь:

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

Я хотел бы взять эту таблицу и построить TreeView в .NET. Таким образом, наличие полного пути для каждого элемента сделало бы его легкой прогулкой.

В противном случае я думал, что могу начать с корневой страницы и продолжать рекурсию по родителям, создавая список дочерних элементов, затем повторяя их и т. д.

Есть ли лучший способ запросить это, чтобы построить эти пути? В этом списке 400 000 записей, поэтому, если есть более эффективный способ, это сэкономит время.

Все это было первоначально в системной базе данных AS400 до 2000 года, а затем превращено в сайт MediaWiki. Я извлекаю данные через API с целью создания интерфейса для базы данных SQL Server.

Я могу выполнять базовые SQL-запросы, объединения, объединения и т. д.

Дайте мне знать, какую еще информацию я могу предоставить, если это неясно


person mreinsmith    schedule 27.12.2016    source источник


Ответы (3)


Вы можете использовать INNER JOIN и LEFT JOIN, если вы используете SQL SERVER MS, и вот как выглядит запрос, который даст вам полный результат (комбинацию) на основе вашего требования:

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

person LONG    schedule 27.12.2016
comment
Спасибо, я попробую через несколько минут и дам вам знать - person mreinsmith; 27.12.2016
comment
Это работает как шарм! Я сделал несколько настроек и опубликую их, но я хочу увидеть, что Алекс скажет на мой комментарий. - person mreinsmith; 27.12.2016
comment
Рад, что смог помочь :) - person LONG; 27.12.2016
comment
Я разместил свои результаты вместе с еще парой вопросов. Дайте мне знать, что вы думаете. Кстати, в любом случае с тем, что вы мне показали, я смогу это сделать. - person mreinsmith; 27.12.2016

Древовидная структура означает рекурсию для общего решения. Пожалуйста, не пытайтесь делать это в sql. Просто возьмите строку данных из sql в список или что-то подобное и заполните рекурсией на языке программирования.

Ваш класс дерева будет выглядеть так:

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

   )

Но я постараюсь объяснить на вашем столе, как это делается. Я предполагаю, что у вас есть какой-то контекст данных (DBML, EDMX и т.д.)

  1. Найдите корень или корни. В вашем случае корневыми будут те номера, которые находятся в ParentID, а не в ChildId.

Запрос, который перечислит ваши корни:

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 => 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 из точки 1, хранящиеся в списке, скажем, ListIds, вы сделаете что-то вроде этого:

    List finaltrees = new List()

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

Теперь у вас есть древовидная структура в finaltrees.

PS:

Я писал код прямо в браузере (C#), возможны опечатки.

person Alex Banu    schedule 27.12.2016
comment
Хорошо, именно так я и собирался это сделать. Можете объяснить, почему так лучше? по моему опыту, такая рекурсия занимает некоторое время. Когда приведенный выше запрос вообще не занимает времени? За исключением, конечно, того, что мне нужно выяснить, как остановить бесконечную рекурсию и придумать уникальные записи. Это то, что я собирался спросить дальше, но я хотел услышать, что вы хотели сказать. - person mreinsmith; 27.12.2016
comment
Спасибо! Позвольте мне запустить это корыто и посмотреть, как оно идет. - person mreinsmith; 28.12.2016
comment
Какую сборку вы имеете в виду для db.YourTable.Select и db.YourTable.Any? Я нашел оператор SELECT с помощью DataTable, но он не принимает предикат и вообще ничего для ЛЮБОГО - person mreinsmith; 28.12.2016

Итак, чтобы уточнить, что я пытаюсь сделать, я работаю с вики-версией, которая не использует пространства имен для установления путей к документам.

Например, если страница имеет глубину 3 уровня в дереве документа, подобном этому

  • RootPage
    • Page01
      • Page02
        • Page03
        • Страница04

Использование пространства имен. Имя (путь) страницы 03: "RootPage:Page01:Page02:Page03"

Я хотел бы сделать то же самое с идентификаторами страниц

Итак, учитывая этот пример, у вас будет

  • PageTitle Путь к идентификатору страницы
  • Корневая страница 001 001
  • Страница01 101 001:101
  • Страница02 201 001:101:201
  • Страница03 301 001:101:201:301
  • Страница04 302 001:101:201:302

Итак, теперь все, что мне нужно сделать, это собрать вместе PagePath.

Есть несколько проблем, которые следует учитывать при работе с этой вики.

  1. Не может быть двух документов с одинаковым НАЗВАНИЕМ
  2. Идентификаторы документов в основном не имеют значения, но удобны в этом случае (по крайней мере, в версии, над которой я работаю).
  3. К счастью, есть список страниц и их «ссылки» или дочерние страницы. Я полагаю, вы бы назвали это МНОГИМ ко МНОГИМ

Ключевой момент, о котором следует помнить, заключается в том, что даже если страница указана как дочерняя для многих других страниц, на самом деле существует только одна, и мне нужна только одна из них в результатах.

Итак, используя пример LONG, я пришел к

Использование этой таблицы:

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 записей.

Когда я запускаю этот запрос к фактическому «списку ссылок», он выполняется около 17 минут и не хватает памяти.

Я изучал вариант MAXRECURSION, но я все еще работаю над ним, не знаю, проблема это или нет.

person mreinsmith    schedule 27.12.2016