【问题标题】:Recursion for multiple Hierarchies and summing all parents多个层次结构的递归并对所有父级求和
【发布时间】:2021-01-07 15:59:52
【问题描述】:

我刚刚开始深入研究 SQL Server。不到一个月前写了我的第一个脚本,所以我的学习曲线很陡峭。

我有一个项目列表表,其中有多个不同深度的层次结构。 这是一个示例。

--To Get Started, Here are some Samples of a components list of nearly 1000 records
CREATE TABLE [Sampletbl]
(
     ID varchar(20), 
     Months smallint, 
     DirectParent varchar(20), 
     HighestParent varchar(20)
)

INSERT INTO Sampletbl (ID, Months, DirectParent, HighestParent)
VALUES ('BERB CPB06',   11, 'BERB CPB18',   'BERB CPB18'),
       ('BERB CPB08',    9, 'BERB CPB18',   'BERB CPB18'),
       ('BERB CPB18',   10, 'Prop',         'BERB CPB18'),
       ('BUXU GNV06',   12, 'BUXU GNV18',   'BUXU GNV98'),
       ('BUXU GNV08',   10, 'BUXU GNV18',   'BUXU GNV98'),
       ('BUXU GNV10',   11, 'BUXU GNV32',   'BUXU GNV98'),
       ('BUXU GNV18',    9, 'BUXU GNV32',   'BUXU GNV98'),
       ('BUXU GNV32',   11, 'BUXU GNV98',   'BUXU GNV98'),
       ('BUXU GNV98',   12, 'Prop',         'BUXU GNV98'),
       ('HYDR ARANN08', 12, 'Prop',         'HYDR ARANN08'), --This and the record below are original, even though they have the same prefix
       ('HYDR ARANN18', 12, 'Prop',         'HYDR ARANN18')

具有“Prop”直接父级的项目是原件 - 它们没有父级。 父母可以有多个孩子。

这是一张深度图。

with ItemsAndTheirPrecedents (ID, DirectParent, Precedents) As
(
Select  ID,
        DirectParent,
        0
From    Sampletbl
Where   DirectParent = 'Prop'
Union All

Select  Sampletbl.ID,
        Sampletbl.DirectParent,
        ItemsAndTheirPrecedents.Precedents + 1
From    Sampletbl
    Join ItemsAndTheirPrecedents on Sampletbl.DirectParent = ItemsAndTheirPrecedents.ID

)

select *
from ItemsAndTheirPrecedents
Order by Precedents
--If the direct Parent is Prop there are no ancestors - this item starts the tree

我想做的是合计每个项目及其所有父项(如果有)的月份。

类似这样的:

ID Months Parent Total Months
BERB CPB06 11 BERB CPB18 21
BERB CPB08 9 BERB CPB18 19
BERB CPB18 10 Prop 10
BUXU GNV06 12 BUXU GNV18 44
BUXU GNV08 10 BUXU GNV18 42
BUXU GNV10 11 BUXU GNV32 34
BUXU GNV18 9 BUXU GNV32 32
BUXU GNV32 11 BUXU GNV98 23
BUXU GNV98 12 Prop 12
HYDR ARANN08 12 Prop 12
HYDR ARANN18 12 Prop 12

在最复杂的项目中,每一层的总数不断增加:

ID Months Total Months
BUXU GNV06 12 44
BUXU GNV18 9 32
BUXU GNV32 11 23
BUXU GNV98 12 12

通过以下递归,我可以获得项目及其直接父项的月总和。

with TotalMonthsForItemsAndParents (ID, Months, DirectParent, TotalMonths) As
(
Select  ID,
        Months,
        DirectParent,
        Months
From    Sampletbl
Where   DirectParent = 'Prop' 

Union All

Select  Sampletbl.ID,
        Sampletbl.Months,
        Sampletbl.DirectParent,
        TotalMonthsForItemsAndParents.Months + Sampletbl.Months
From    Sampletbl
    Join TotalMonthsForItemsAndParents on Sampletbl.DirectParent = TotalMonthsForItemsAndParents.ID

)

select * 
from TotalMonthsForItemsAndParents
Order by ID

但是我不知道如何让它看起来多代并将它们总结到每个项目的级别。

谢谢你帮助我。我知道递归 cte 的主题遍布这个网站,但我找不到我要找的东西。老实说,我花了整整一周的时间。

【问题讨论】:

  • 抱歉,第二张表在预览中有效,但在帖子中无效。
  • 感谢 Marc 修复那张桌子!

标签: sql-server recursion


【解决方案1】:

按照您编写最后一个查询的方式,它只考虑了层次结构中紧接在前的祖先。这是一个跟踪所有祖先的技巧:

with TotalMonthsForItemsAndParents (ID, Months, DirectParent, Months, RunningTotal) As
(
    Select  ID,
            Months,
            DirectParent,
            Months,
            Months as RunningTotal 
    From    Sampletbl
    Where   DirectParent = 'Prop' 

    Union All

    Select  Sampletbl.ID,
            Sampletbl.Months,
            Sampletbl.DirectParent,
            Sampletbl.Months,
            TotalMonthsForItemsAndParents.RunningTotal + Sampletbl.Months
    From    Sampletbl
    Join TotalMonthsForItemsAndParents on Sampletbl.DirectParent = TotalMonthsForItemsAndParents.ID
)

select *
from TotalMonthsForItemsAndParents
Order by ID;

对于基本情况,RunningTotal 值就是该行的 Months 值。对于后续(即递归步骤),它是该级别的 Months 值和上一个级别的 RunningTotal 的总和。

【讨论】:

  • 谢谢!很简单。那么cte的第一个描述中totals列的命名约定是我哪里出错了?
  • 不完全。为了清楚起见,我对我的答案做了一个小的编辑。之前,您对 Total Months 的定义被定义为 parent.Months + child.Months。在这里,我将其定义为parent.RunningTotal + child.Months
【解决方案2】:

试试这个:

;with TotalMonthsForItemsAndParents (
    ID
    , Months
    , DirectParent
    , TotalMonths
    , lvl )
As
(
    -- Anchor member
    Select  ID,
            Months,
            DirectParent,
            Months as TotalMonths,
            lvl = 1
    From    Sampletbl
        
    Union All

     -- Recursive member: the direct parent (if any) of the previous recursion
    Select  Sampletbl.ID,
            Sampletbl.Months,
            Sampletbl.DirectParent,
            TotalMonths = total.TotalMonths + Sampletbl.Months,
            lvl = lvl + 1
    From    Sampletbl 
    Join    TotalMonthsForItemsAndParents as total on Sampletbl.DirectParent = total.ID
)
select ID
        , Months
        , DirectParent
        , TotalMonths
from (
    -- use row_number() so as to keep only the latest recursion 
    select ID
        , Months
        , DirectParent
        , TotalMonths
        , row_number() over (partition by ID order by lvl desc) as rn
    from TotalMonthsForItemsAndParents
) t
where t.rn = 1
Order by ID

Demo here

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-07-04
    • 1970-01-01
    • 1970-01-01
    • 2021-06-14
    • 2023-03-31
    • 1970-01-01
    • 2013-11-07
    相关资源
    最近更新 更多