2009-12-15 69 views
2

我需要重新計算BOM結構中的數量,該數量僅保證葉節點的正確數量值。自下而上計算層次結構中的聚合值

如果上面有點模糊不要擔心,下面是一個簡單的例子。

考慮一個分層表,定義了三列:當前記錄中的ID(PK),PID(層次結構中的父ID)和Q(數量)。

set nocount on 
declare @results table(
     ID nvarchar(3), 
     PID nvarchar(3), 
     Q int, 
     lvl int, 
     ord int 
    ) 

ord列僅用於排序結果。

lvl列定義層次結構中當前記錄的級別。

對於那些想知道如何維護這些列的人來說,@results表在現實世界中由一個完成所有技巧的函數填充;對於這個例子,他們將被賦予硬編碼值。

現在,問題是隻有層級中的葉級節點才能保證正確的Q值。對於其他節點Q可能會或可能沒有正確定義。我的任務是重新計算這些節點的Q值。

的樣本數據:

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 

正如你可以看到,數量的B,C1是錯誤的:他們是15和5,但應該是10和7:

select * from @results order by ord 

這裏的初始數據:

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 

最後一個問題:有沒有辦法在SET-更新該表所以所有的數量都用自下而上的兒童節點的總量進行更新?

我想出了下面可以看到最好的,但它不是一套基於:

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 

這給正確的數量:

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 

謝謝!

回答

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'它的工作原理,但我必須咀嚼它一段時間:) – 2009-12-15 16:41:45