FOREACH Рекурсивный оператор SQL - PullRequest
2 голосов
/ 23 октября 2011

У меня есть, казалось бы, простая проблема, но решения, которые я пробовал на сегодняшний день, оставили меня желать в областях исполнения.Скорости кажутся хорошими для небольших (<10000) наборов данных, но быстро растут все больше и больше времени. </p>

В SQL Server 2008 R2 у меня есть таблица с четырьмя столбцами: Id, ParentId, ControlNum, ParentControlNum.

Заполняется информация Id и ParentId. Id всегда имеет значение, ParentId равен нулю, если у строки нет родителя, в противном случае это значение Id в таблице, которая представляетродительская строка.

Проблема в том, что Id и ParentIds повсюду.Все идентификаторы добавляются в таблицу, а затем обрабатываются для добавления потомков.Это установленная часть проблемы, и ее нельзя изменить.

Что мне нужно сделать, это сгенерировать значения ControlNum, чтобы подчиняться отношению Parent Child.Моя текущая логика использует немного команд C # и SQL SELECT / UPDATE для достижения этой цели, но, как уже упоминалось, производительность вызывает большое беспокойство.

В псевдокоде

Select all Id's where the parent Id is null (All root entries)
Foreach (Id)
   GenerateControlNum(Id, CurrentCounterValue, CurrentCounterValue)

GenerateControlNum(Id, CurrentCounterValue, ParentCounterValue)
    Set Id's ControlNum to CurrentCounterValue
    Set Id's ParentControlNum to CurrentCounterValue

    Increment CurrentCounterValue

    Select All Id's where ParentId == Id (All my direct children)
    Foreach (ChildId)
        GenerateControlNum(ChildId, CurrentCounterValue, Id's ControlNum);

Базовая линия пытаетсясделайте это быстрее, в идеале будет полностью в SQL.Я пытаюсь повторить CTE, заполненный RootIds, а затем просматриваю их с помощью оператора MERGE, но мне просто не удается заставить значение счетчика работать должным образом для установки значений ControlNum.

Этовозможно ли это в SQL или это слишком процедурный тип обработки?

Пример таблицы данных о том, как он выполняется в настоящее время: ДО

ID                                      ParentId                                ControlNum  ParentControlNum
8C821027-A6F9-E011-AB48-B499BAE13A62    756F981E-A6F9-E011-AB48-B499BAE13A62    0           NULL
D7DB6033-A6F9-E011-AB48-B499BAE13A62    756F981E-A6F9-E011-AB48-B499BAE13A62    0           NULL
D2E36033-A6F9-E011-AB48-B499BAE13A62    C9E36033-A6F9-E011-AB48-B499BAE13A62    0           NULL
8FE66033-A6F9-E011-AB48-B499BAE13A62    58E66033-A6F9-E011-AB48-B499BAE13A62    0           NULL
37EC6033-A6F9-E011-AB48-B499BAE13A62    2FEC6033-A6F9-E011-AB48-B499BAE13A62    0           NULL
41EC6033-A6F9-E011-AB48-B499BAE13A62    2FEC6033-A6F9-E011-AB48-B499BAE13A62    0           NULL 
DDED6033-A6F9-E011-AB48-B499BAE13A62    BCED6033-A6F9-E011-AB48-B499BAE13A62    0           NULL
DC69981E-A6F9-E011-AB48-B499BAE13A62    NULL                                    0           NULL
166A981E-A6F9-E011-AB48-B499BAE13A62    NULL                                    0           NULL
4D6A981E-A6F9-E011-AB48-B499BAE13A62    NULL                                    0           NULL
856A981E-A6F9-E011-AB48-B499BAE13A62    NULL                                    0           NULL
F56A981E-A6F9-E011-AB48-B499BAE13A62    NULL                                    0           NULL
2E6B981E-A6F9-E011-AB48-B499BAE13A62    NULL                                    0           NULL
666B981E-A6F9-E011-AB48-B499BAE13A62    NULL                                    0           NULL
9D6B981E-A6F9-E011-AB48-B499BAE13A62    NULL                                    0           NULL

ПОСЛЕ

ID                                      ParentId                                ControlNum  ParentControlNum
8C821027-A6F9-E011-AB48-B499BAE13A62    756F981E-A6F9-E011-AB48-B499BAE13A62    22          21
D7DB6033-A6F9-E011-AB48-B499BAE13A62    756F981E-A6F9-E011-AB48-B499BAE13A62    24          21
D2E36033-A6F9-E011-AB48-B499BAE13A62    C9E36033-A6F9-E011-AB48-B499BAE13A62    58          57
8FE66033-A6F9-E011-AB48-B499BAE13A62    58E66033-A6F9-E011-AB48-B499BAE13A62    69          68
37EC6033-A6F9-E011-AB48-B499BAE13A62    2FEC6033-A6F9-E011-AB48-B499BAE13A62    86          85
41EC6033-A6F9-E011-AB48-B499BAE13A62    2FEC6033-A6F9-E011-AB48-B499BAE13A62    88          85
DDED6033-A6F9-E011-AB48-B499BAE13A62    BCED6033-A6F9-E011-AB48-B499BAE13A62    95          94
DC69981E-A6F9-E011-AB48-B499BAE13A62    NULL                                    0           0
166A981E-A6F9-E011-AB48-B499BAE13A62    NULL                                    1           1
4D6A981E-A6F9-E011-AB48-B499BAE13A62    NULL                                    2           2
856A981E-A6F9-E011-AB48-B499BAE13A62    NULL                                    3           3
F56A981E-A6F9-E011-AB48-B499BAE13A62    NULL                                    4           4
2E6B981E-A6F9-E011-AB48-B499BAE13A62    NULL                                    5           5
666B981E-A6F9-E011-AB48-B499BAE13A62    NULL                                    6           6
9D6B981E-A6F9-E011-AB48-B499BAE13A62    NULL                                    7           7

У меня сейчас 104 набора данных, так что это только первые 15. Объекты без родителей находятся внизу, это примеры корневых записей, в которых их контрольный номер и родительский контрольный номер установлены на одно и то же значение.В верхней части таблицы мы видим несколько объектов, которые имеют одинаковых родителей и поэтому имеют совпадающие родительские контрольные номера и сами довольно близкие контрольные номера (должна быть строка между ControlNum 22 и 24, например, также из родительского 21-го. То же самое для 86до 88 прыжков, они просто не находятся в таблице рядом друг с другом).

Надеюсь, это делает это более ясным.

РЕДАКТИРОВАТЬ: больше ясности на основе ответа, данного Микаэлем

Ниже приведены значения ControlNum, отображаемые в иерархии на основе их данных Id и ParentId.Обычно они перечислены в 1, 2, 3 ... 8, но проще не загромождать дисплей сообщениями (потомками).

1
  4
    7
    8
  5
2
  6
3

Мне нужно

1
  2
    3
    4
  5
6
  7
8

Вот почему я использовал рекурсию: мне нужно присвоить ControlNum корневому объекту, а затем следующим объектом должен быть его первый дочерний объект, за которым следуют дочерние объекты и т. Д.к следующему корневому объекту.

Полагаю, то, что я говорю, это то, что сначала это ширина, а то, что мне нужно, это глубина вначале.

1 Ответ

3 голосов
/ 23 октября 2011

Не уверен, что я выполнил все ваши требования, но вот начало.Подскажите, если он делает то, что вы хотите, или числа не сгенерированы правильно.

;with C as
(
  select ID,
         ParentID,
         ControlNum,
         ParentControlNum,
         row_number() over(order by ParentID, ID) - 1 as rn
  from YourTable
)  
update C1
set ControlNum = C1.rn,
    ParentControlNum = case when C1.ParentID is null 
                         then C1.rn
                         else C2.rn 
                       end
from C as C1
  left outer join C as C2
    on C1.ParentID = C2.ID

Запустите его на SE-Data с немного измененным вводом: http://data.stackexchange.com/stackoverflow/q/115625/

Версия2

Сначала рекурсивный CTE R, который строит строку, которая будет использоваться в качестве порядка при генерации значений для ControlNum.После этого это почти то же самое, что и выше.

;with R as
(
  select ID,
         ParentID,
         cast(ID as varchar(max)) as Sort
  from YourTable
  where ParentID is null
  union all
  select T.ID,
         T.ParentID,
         R.Sort+cast(T.ID as varchar(max))
  from YourTable as T
    inner join R
      on R.ID = T.ParentID
),
C as
(
  select ID,
         ParentID,
         row_number() over(order by Sort) - 1 as rn
  from R
)  
update T
set ControlNum = C1.rn,
    ParentControlNum = case when C1.ParentID is null 
                         then C1.rn
                         else C2.rn 
                       end
from YourTable as T
  inner join C as C1
    on T.ID = C1.ID
  left outer join C as C2
    on T.ParentID = C2.ID

Тест здесь: http://data.stackexchange.com/stackoverflow/q/115626/

Примечание: я полагаю, что это один раз, что вы будете делать с некоторыми данными, потому что выбудет трудно добавлять новые узлы и в то же время поддерживать нумерацию следующим образом.Например, если вы добавите новый дочерний узел к первому узлу, вам нужно будет назначить все ControlNum += 1 для всех узлов «ниже» и переназначить все ParentControlNum.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...