递归自连接求和

Sum on a Recursive Self-Join

我在以下位置找到了一篇非常有用的文章: Simplest way to do a recursive self-join in SQL Server?

假设在这个例子中有另一个名为 "Quantity" 的列存储整数,如下所示:

PersonID | Initials | ParentID  |  Quantity
1          CJ         NULL            1
2          EB         1               2
3          MB         1               1
4          SW         2               1
5          YT         NULL            1
6          IS         5               1

如果我请求 CJ 的层次结构,它将是

PersonID | Initials | ParentID  |  Quantity |  HasSubordinate
1          CJ         NULL            2            1
2          EB         1               1            1
3          MB         1               1            1
4          SW         2               1            0

HasSubordinate 列指定层次结构中的最后一个个体。我想显示层次结构中的最后一个人,并将前一行的数量相乘。在这种情况下,数量将为 2 (2 x 1 x 1 x 1 = 2)。

PersonID | Initials | ParentID  |  Quantity |  HasSubordinate
4          SW         2               2            0

我的代码:

WITH    q AS 
        (
        SELECT  *
        FROM    mytable
        WHERE   PersonID = 1
        UNION ALL
        SELECT  m.*
        FROM    mytable m
        JOIN    q
        ON      m.parentID = q.PersonID
        )
SELECT  *
FROM    q
WHERE HasSubordinate = 0

非常感谢任何帮助!!

您可以在递归 cte 中添加一个新字段并在迭代时相乘:

WITH    q AS 
        (
        SELECT  *,Quantity AS Tot_Qty
        FROM    mytable
        WHERE   PersonID = 1
        UNION  ALL
        SELECT  m.*,m.Quantity * q.Tot_Qty AS Tot_Qty
        FROM    mytable m
        JOIN    q
        ON      m.parentID = q.PersonID
        )
SELECT  *
FROM    q
WHERE HasSubordinate = 0

注意:这会让您 2 x 1 x 1 而不是 2 x 1 x 1 x 1 因为您使用的是 ParentID.

时不时地,有人抱怨没有 MULT 聚合函数。也许有一天会有,但在那之前,我们必须作弊。以下是基于 LOG(a * b * c ) = LOG( a ) + LOG( b ) + LOG( c ) 的事实。不幸的是,它需要一个额外的 CTE 级别(虽然不是递归的)但它最终会得出一个答案。

with
List( PersonID, Initials, ParentID, Qty )as(
    select  1, 'CJ', null,  1 union all
    select  2, 'EB', 1,     2 union all
    select  3, 'MB', 1,     3 union all
    select  4, 'SW', 2,     4 union all
    select  5, 'YT', null,  2 union all
    select  6, 'IS', 5,     5
),
CTE( PersonID, Initials, ParentID, Qty, Root )as(
    select  l.PersonID, l.Initials, l.ParentID, l.Qty, l.PersonID
    from    List    l
    where   l.ParentID is null
        --and l.Initials = 'CJ'
    union all
    select  l.PersonID, l.Initials, l.ParentID, l.Qty, c.Root
    from    CTE     c
    join    List    l
        on  l.ParentID = c.PersonID
),
Logs( PersonID, Initials, ParentID, Qty, Root, SumLog )as(
    select *, sum( log( Qty )) over( partition by Root)
    from CTE
)
select  *, exp( SumLog ) as Mult
from    Logs
order by PersonID;

生成此结果:

PersonID Initials ParentID Qty Root SumLog           Mult
-------- -------- -------- --- ---- ---------------- ----
1        CJ       NULL       1    1 3.17805383034795   24
2        EB       1          2    1 3.17805383034795   24
3        MB       1          3    1 3.17805383034795   24
4        SW       2          4    1 3.17805383034795   24
5        YT       NULL       2    5 2.30258509299405   10
6        IS       5          5    5 2.30258509299405   10

这满足所述要求,拉出最后一行将使所有 QTY 的总数相乘——它们都具有该值。也许聪明的人可以生成 运行 总数。我会把它留作练习(意思是我懒得自己尝试)。