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.

È stato utile?

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ì.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top