Каков наилучший способ группировки, агрегирования и суммирования данных дерева? - PullRequest
2 голосов
/ 23 октября 2009

Имеется таблица с самообращением

Item 
-------------
Id (pk)
ParentId (fk)

Со связанной таблицей связанных значений

ItemValue
-------------
ItemId (fk)
Amount

И некоторые образцы данных

Item                       ItemValues 
Id      ParentId           ItemId      Amount
--------------------       ----------------------
1       null               1           10
2       1                  3           40
3       1                  3           20
4       2                  4           10
5       2                  5           30
6       null
7       6
8       7

Мне нужен спрок, чтобы взять Item.Id и вернуть прямых детей с суммами всех ItemValue.Amounts для них, их детей и их детей на всем протяжении дерева.

Например, если передано 1, дерево будет 2, 3, 4, 5, прямые дочерние элементы 2, 3, на выходе будет

 ItemId    Amount
 ------------------
 2         40     (values from ItemIds 4 & 5)
 3         60     (values from ItemId 3)

Какие подходы следует применять, чтобы добиться такого поведения?

Я рассматриваю возможность использования CTE, но мне интересно, есть ли лучший / более быстрый подход.

Ответы [ 3 ]

6 голосов
/ 23 октября 2009

Рекурсивный CTE, подобный этому, будет работать, предполагая, что ваша иерархия не слишком глубока:

declare @ParentId int;
set @ParentId = 1;

;with 
  Recurse as (
    select 
      a.Id as DirectChildId
    , a.Id
    from Item a 
    where ParentId = @ParentId
    union all
    select
      b.DirectChildId
    , a.Id
    from Item a 
    join Recurse b on b.Id = a.ParentId
    )
select
  a.DirectChildId, sum(b.Amount) as Amount
from Recurse a
left join ItemValues b on a.Id = b.ItemId
group by
  DirectChildId;

Метод без использования CTE потребует некоторой формы итерации, на основе курсора или иным образом. Так как это хранимый процесс, это возможно, и если есть много данных, через которые он может быть обработан, он, вероятно, будет лучше масштабироваться, при условии, что вы нарезаете данные соответствующим образом.

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

Если кластерный индекс уже находится на ParentId, добавьте некластеризованный индекс на Id. Вместе они будут практически эквивалентны вышесказанному. Для ItemValues ​​вам может потребоваться индекс для (ItemId) INCLUDE (Amount), если фактическая таблица шире, чем эта.

0 голосов
/ 23 октября 2009

Обрабатывается ли это в базе данных? Я бы предложил внести необходимые данные в ваш BLL и выполнить там рекурсию.

0 голосов
/ 23 октября 2009

Не могли бы вы сохранить ваши данные, как в модели вложенного набора (здесь MySQL ссылка , но идеи являются общими для всех баз данных)? Если это так, то операции по поиску искомого значения будут довольно простыми.

...