2009-12-15 4 views
2

J'ai besoin de recalculer les quantités dans une structure de nomenclature qui ne garantit que les valeurs de quantité correctes pour les nœuds feuilles.Calcul des valeurs agrégées dans une structure hiérarchique bottom-up

Si ce qui précède est un peu flou, ne vous inquiétez pas, voici un exemple simplifié.

Considérons une table hiérarchique définissant trois colonnes: ID (PK), PID (ID parent dans la hiérarchie) et Q (quantité - de quelque chose, pour l'enregistrement en cours). La colonne ord sert uniquement à trier les résultats. La colonne lvl définit le niveau de l'enregistrement en cours dans la hiérarchie.

Pour ceux qui se demandent comment ces colonnes sont maintenues, la table @results est peuplée dans le monde réel par une fonction qui fait toutes les astuces; pour cet exemple, ils recevront des valeurs codées en dur.

Maintenant, le problème est que les valeurs Q correctes sont garanties uniquement pour les nœuds de niveau feuille dans la hiérarchie. Pour les autres nœuds Q peut ou peut ne pas être correctement défini. Ma tâche consiste à recalculer les valeurs Q pour ces nœuds.

données Exemple:

insert into @results(ord, lvl, ID, PID, Q) 
select 1, 0, 'A' as ID, null as PID, null as Q union 
select 2, 1, 'B' , 'A' , 15 union 
select 3, 1, 'C' , 'A' , 10 union 
select 4, 2, 'B1' , 'B' , 6 union 
select 5, 2, 'B2' , 'B' , 4 union 
select 6, 2, 'C1' , 'C' , 5 union 
select 7, 2, 'C2' , 'C' , 3 union 
select 8, 3, 'C11', 'C1', 4 union 
select 9, 3, 'C12', 'C1', 3 

Comme vous pouvez le voir, les quantités pour B et C1 sont mal: ils sont 15 et 5, mais devraient être 10 et 7:

select * from @results order by ord 

Voici la données initiales:

ID PID   Q   lvl   ord 
---- ---- ----------- ----------- ----------- 
A NULL  NULL   0   1 
B A    15   1   2 
C A    10   1   3 
B1 B    6   2   4 
B2 B    4   2   5 
C1 C    5   2   6 
C2 C    3   2   7 
C11 C1    4   3   8 
C12 C1    3   3   9 

Enfin, la question suivante: est-il possible de mettre à jour ce tableau dans un SEt- manière basée de sorte que toutes les quantités sont mises à jour avec des quantités additionnées ascendantes de nœuds enfants?

Le meilleur que je suis venu avec on peut le voir ci-dessous, mais il est pas encore défini basé:

declare @level int 

select @level = max(lvl) from @results 

while (@level > 0) 
begin 
    update r set Q = s.SumQ 
    from @results r inner join (
      select PID, sum(Q) as SumQ 
      from @results 
      where lvl = @level group by PID 
     ) s on (r.ID = s.PID) 

    set @level = @level - 1 
end 

select * from @results 

qui donne les quantités correctes:

ID PID   Q   lvl   ord 
---- ---- ----------- ----------- ----------- 
A NULL   20   0   1 
B A    10   1   2 
C A    10   1   3 
B1 B    6   2   4 
B2 B    4   2   5 
C1 C    7   2   6 
C2 C    3   2   7 
C11 C1    4   3   8 
C12 C1    3   3   9 

Merci!

Répondre

2
;WITH q AS 
     (
     SELECT *, id AS initial 
     FROM @results 
     UNION ALL 
     SELECT r.*, initial 
     FROM q 
     JOIN @results r 
     ON  r.pid = q.id 
     ) 
UPDATE ru 
SET  q = qn.nq 
FROM @results ru 
JOIN (
     SELECT initial, 
       SUM(rq) AS nq 
     FROM q 
     LEFT JOIN 
       (
       SELECT id, 
         CASE 
         WHEN EXISTS 
         (
         SELECT NULL 
         FROM @results ri 
         WHERE ri.pid = r.id 
         ) 
         THEN NULL 
         ELSE q 
         END AS rq 
       FROM @results r 
       ) r 
     ON  r.id = q.id 
       AND r.id <> q.initial 
     GROUP BY 
       q.initial 
     ) qn 
ON  ru.id = qn.initial 
     AND qn.nq IS NOT NULL 
+0

+1 coz 'ça marche mais je vais devoir le mâcher pendant un moment :) –

Questions connexes