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

StackOverflow https://stackoverflow.com/questions/1610893

Вопрос

Дана таблица с самостоятельной ссылкой

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

Мне нужен sproc, чтобы взять 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, но мне интересно, есть ли лучший / более быстрый подход.

Это было полезно?

Решение

Подобный рекурсивный 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.Вместе они будут практически эквивалентны вышеперечисленным.Для значений ITEM вам может потребоваться включить индекс (ItemId) INCLUDE (Amount), если фактическая таблица шире этой.

Другие советы

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top