【问题标题】:Sum on a Recursive Self-Join递归自连接求和
【发布时间】:2015-04-25 04:12:21
【问题描述】:

我发现了一篇非常有用的文章,位于: 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

非常感谢任何帮助!

【问题讨论】:

    标签: sql recursion


    【解决方案1】:

    您可以在递归 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

    【讨论】:

    • 太棒了!这正是我一直在寻找的,但由于某种原因无法想出它。请注意,最初有一个错误抱怨不兼容的类型,所以我不得不将 Quantity 硬编码为小数。谢谢你的帮助!!
    • 我认为这不是正确的答案。例如,如果您将数量从 1 更改为 2,您应该得到 2*2*2*2 = 16,但此查询将返回 8
    • @GiorgiNakeuri 同意,因此我的答案底部的注释,我正在考虑替代方案,但在被接受后就不管了。
    • 啊,如果只有被乘数的总和这么容易!这是行不通的,因为下一层的所有行都可以看到它们的根,而不是彼此。递归 cte 的输出使您看起来可以将它们全部相乘,但并非如此。
    • @TommCatt 我确实意识到我没有引用递归产品,所以已经更新了。只要每个父母有 1 个孩子,例如,在示例数据中,PersonID = 3 就不包含在 PersonID = 4 的总数中,因为它们不在该记录的父/子行中。这就是我打算在答案底部指出的内容,但本可以更好地解释。
    【解决方案2】:

    时不时地,有人抱怨没有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 的总数相乘 - 它们都具有该值。也许聪明的人可以生成一个运行总计。我会把它留作练习(这意味着我懒得自己尝试)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-05-28
      • 1970-01-01
      • 2013-08-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-07-31
      相关资源
      最近更新 更多