Qual è il modo migliore per raggruppare e aggregare e sommare i dati dell'albero?
-
05-07-2019 - |
Domanda
Data una tabella autoreferenziale
Item
-------------
Id (pk)
ParentId (fk)
Con una tabella correlata di valori associati
ItemValue
-------------
ItemId (fk)
Amount
E alcuni dati di esempio
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
Ho bisogno di uno sproc per prendere Item.Id
e restituire i figli diretti con somme di tutti ItemValue.Amounts
per loro, i loro figli e i loro figli fino in fondo giù per l'albero.
Ad esempio, se 1
viene passato, l'albero sarebbe 2, 3, 4, 5
i figli diretti saranno 2, 3
l'output sarebbe
ItemId Amount
------------------
2 40 (values from ItemIds 4 & 5)
3 60 (values from ItemId 3)
Che tipo di approccio dovrebbe essere applicato per raggiungere questo comportamento?
Sto pensando di utilizzare un CTE, ma mi chiedo se esiste un approccio migliore / più veloce.
Soluzione
Un CTE ricorsivo come questo funzionerebbe, supponendo che la tua gerarchia non vada troppo in profondità:
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 metodo non CTE richiederebbe una qualche forma di iterazione, basata sul cursore o altro. Dal momento che è un proc memorizzato, è una possibilità e se ci sono molti dati da ricorrere, probabilmente si ridimensionerebbe meglio, fintanto che i dati verranno suddivisi in modo appropriato.
Se l'indice cluster è su ID, aggiungere un indice non cluster su ParentId. Come indice di copertura, soddisferà la ricerca iniziale senza la ricerca di un segnalibro. L'indice cluster aiuterà quindi con l'unione ricorsiva.
Se invece l'indice cluster è già su ParentId, aggiungi un indice non cluster su Id. Insieme, saranno praticamente equivalenti a quanto sopra. Per ItemValues, potresti voler un indice su (ItemId) INCLUDE (Amount), se la tabella effettiva è più ampia di questa.
Altri suggerimenti
Potresti archiviare i tuoi dati come nel modello di set nidificato (ecco un MySQL riferimento ma le idee sono generiche in tutti i database)? In tal caso, le operazioni per trovare il valore che stai cercando sarebbero abbastanza semplici.
Questo deve essere gestito nel database? Suggerirei di inserire i dati necessari nel tuo BLL ed eseguire la ricorsione lì.