¿Cuál es la mejor manera de agrupar, agregar y sumar datos de árbol?
-
05-07-2019 - |
Pregunta
Dada una tabla de auto referencias
Item
-------------
Id (pk)
ParentId (fk)
Con una tabla relacionada de valores asociados
ItemValue
-------------
ItemId (fk)
Amount
Y algunos datos de muestra
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
Necesito un sproc para tomar Item.Id
y devolver los hijos directos con las sumas de todos los ItemValue.Amounts
para ellos, sus hijos y sus hijos hasta el final abajo del arbol
Por ejemplo, si se pasa 1
, el árbol sería 2, 3, 4, 5
los hijos directos son 2, 3
la salida sería
ItemId Amount
------------------
2 40 (values from ItemIds 4 & 5)
3 60 (values from ItemId 3)
¿Qué tipo de enfoques deben aplicarse para lograr este comportamiento?
Estoy considerando usar un CTE, pero me pregunto si hay un enfoque mejor / más rápido.
Solución
Un CTE recursivo como este funcionaría, suponiendo que su jerarquía no sea demasiado profunda:
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;
Un método que no sea CTE requeriría alguna forma de iteración, basada en el cursor o de otra manera. Dado que es un proceso almacenado, es una posibilidad, y si hay muchos datos que revisar, probablemente se escalará mejor, siempre y cuando corte los datos adecuadamente.
Si el índice agrupado está en Id, agregue un índice no agrupado en ParentId. Como índice de cobertura, satisfará la búsqueda inicial sin una búsqueda de marcadores. El índice agrupado ayudará con la unión recursiva.
Si el índice agrupado ya está en ParentId en su lugar, agregue un índice no agrupado en Id. Juntos, serán prácticamente equivalentes a los anteriores. Para ItemValues, es posible que desee un índice en (ItemId) INCLUDE (Amount), si la tabla real es más ancha que esto.
Otros consejos
¿Podría almacenar sus datos como en el modelo de conjunto anidado? "> referencia pero las ideas son genéricas en todas las bases de datos)? Si es así, las operaciones para encontrar el valor que está buscando serían bastante simples.
¿Esto tiene que ser manejado en la base de datos? Sugeriría traer los datos necesarios a su BLL y realizar la recursión allí.