代码之家  ›  专栏  ›  技术社区  ›  Sorin Comanescu

自下而上计算分层结构中的聚合值

  •  2
  • Sorin Comanescu  · 技术社区  · 16 年前

    我需要在BOM表结构中重新计算数量,该结构只保证叶节点的数量值正确。

    如果上面有点模糊,不要担心,下面是一个简化的例子。

    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
    

    最后,问题是: 是否有任何方法以基于集合的方式更新此表,以便使用自下而上的子节点合计数量更新所有数量?

    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
    

    谢谢

    1 回复  |  直到 16 年前
        1
  •  2
  •   Quassnoi    16 年前
    ;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
    
    推荐文章