条件满足后停止递归

时间:2015-10-20 18:01:45

标签: sql sql-server recursion common-table-expression

我正在使用CTE来爆炸材料清单,并且需要找到所有具有递归组件的材料。

我尝试的是通过将子节点中的BOM_Level设置为最大边界来限制周期(级别)的深度:

exec        pr_sys_drop_object '#BOMExploded'
;with BOM
as
(
    select 
        Prod_Plant_CD
    ,   Demand_Plant_CD
    ,   material_cd
    ,   component_cd
    ,   component_quantity
    ,   component_quantity   AS Calculated_Component_Quantity
    ,   BOM_Level
    ,   Demand_Quantity
    ,   CONVERT(float,1)     AS Produced_Ratio
    ,   Material_CD          AS Demand_Material_CD
    from #firstLevel a
    UNION ALL
    SELECT
        b.Plant_CD as 'Prod_Plant_CD' 
    ,   a.Demand_Plant_CD
    ,   b.Material_CD
    ,   b.Component_CD
    ,   b.component_quantity
    ,   b.component_quantity
    ,   a.BOM_Level + 1
    ,   a.Demand_Quantity
    ,   a.Produced_Ratio * a.Component_Quantity      -- Produced Quantity for the current level = Produced Quantity (level -1) * Component_Quantity (level -1)
    ,   a.Demand_Material_CD
    FROM BOM a 
    inner join #BOM_ProdVersion_Priority b  
        on a.component_cd = b.material_cd
    inner join #base_routes c
        on a.Demand_Plant_CD = c.Recipient_Plant_CD 
       and b.Plant_CD        = c.Source_Plant_CD
       and c.Material_CD     = b.Material_CD   -- Need to have material_cd to link
    where b.Material_CD != b.Component_CD
    and   b.Component_Quantity > 0
    and   BOM_Level < 5 -- Give the max number of levels deep we are allowed to cyncle to
)

select *
into #BOMExploded
from BOM
OPTION (MAXRECURSION 20)

然而,使用此方法需要后处理来定位何时启动递归组件级别的循环,然后返回跟踪。

如果在某种情况下CTE递归查询如何停止?

ie. when top-level material_cd = component_cd for a deeper BOM_Level

1 个答案:

答案 0 :(得分:0)

如果我理解正确,你不需要停留在某个深度/等级,或者你想要停在一定的水平,但你也需要停下来,以防你重复开始骑行材料。

如果是以下递归路径:mat_1 - &gt; mat_2 - &gt; mat_3 - &gt; mat_1,您可能希望在此之前停止mat_1重新开始循环到mat_2,依此类推。

如果这是正确的,那么最好的办法是在递归查询中添加一个Path字段,跟踪它在递归路径下移动时找到的每个字词:

exec        pr_sys_drop_object '#BOMExploded'
;with BOM
as
(
    select 
        Prod_Plant_CD
    ,   Demand_Plant_CD
    ,   material_cd
    ,   component_cd
    ,   component_quantity
    ,   component_quantity   AS Calculated_Component_Quantity
    ,   BOM_Level
    ,   Demand_Quantity
    ,   CONVERT(float,1)     AS Produced_Ratio
    ,   Material_CD          AS Demand_Material_CD
    ,   CAST(material_cd AS VARCHAR(100)) AS Path
    from #firstLevel a
    UNION ALL
    SELECT
        b.Plant_CD as 'Prod_Plant_CD' 
    ,   a.Demand_Plant_CD
    ,   b.Material_CD
    ,   b.Component_CD
    ,   b.component_quantity
    ,   b.component_quantity
    ,   a.BOM_Level + 1
    ,   a.Demand_Quantity
    ,   a.Produced_Ratio * a.Component_Quantity      -- Produced Quantity for the current level = Produced Quantity (level -1) * Component_Quantity (level -1)
    ,   a.Demand_Material_CD
    ,   a.Path + '|' + b.material_cd
    FROM BOM a 
    inner join #BOM_ProdVersion_Priority b  
        on a.component_cd = b.material_cd
    inner join #base_routes c
        on a.Demand_Plant_CD = c.Recipient_Plant_CD 
       and b.Plant_CD        = c.Source_Plant_CD
       and c.Material_CD     = b.Material_CD   -- Need to have material_cd to link
    where b.Material_CD != b.Component_CD
    and   b.Component_Quantity > 0
    and   BOM_Level < 5 -- Give the max number of levels deep we are allowed to cyncle to
    and   a.path NOT LIKE '%' + b.material_cd  + '%'
)

select *
into #BOMExploded
from BOM
OPTION (MAXRECURSION 20)

现在,您有一个以管道分隔的path,您可以测试当前的material_cd以查看它是否已在path中。如果是,则结束递归的那一段以防止循环。

更新以包含我们捕获material_cd周期的版本,并仅在递归结束时报告这些版本:

exec        pr_sys_drop_object '#BOMExploded'
;with BOM
as
(
    select 
        Prod_Plant_CD
    ,   Demand_Plant_CD
    ,   material_cd
    ,   component_cd
    ,   component_quantity
    ,   component_quantity   AS Calculated_Component_Quantity
    ,   BOM_Level
    ,   Demand_Quantity
    ,   CONVERT(float,1)     AS Produced_Ratio
    ,   Material_CD          AS Demand_Material_CD
    ,   CAST(material_cd AS VARCHAR(100)) AS Path
    ,   CAST(NULL AS CHAR(5)) as Cycle_Flag
    ,   0 as Cycle_Depth
    from #firstLevel a
    UNION ALL
    SELECT
        b.Plant_CD as 'Prod_Plant_CD' 
    ,   a.Demand_Plant_CD
    ,   b.Material_CD
    ,   b.Component_CD
    ,   b.component_quantity
    ,   b.component_quantity
    ,   a.BOM_Level + 1
    ,   a.Demand_Quantity
    ,   a.Produced_Ratio * a.Component_Quantity      -- Produced Quantity for the current level = Produced Quantity (level -1) * Component_Quantity (level -1)
    ,   a.Demand_Material_CD
    ,   a.Path + '|' + b.material_cd
    ,   CASE WHEN a.path NOT LIKE '%' + b.material_cd  + '%' then 'Cycle' END AS Cycle_Flag,
    ,   CASE WHEN a.path NOT LIKE '%' + b.material_cd  + '%' then a.Cycle_Depth + 1 END as Cycle_Depth
    FROM BOM a 
    inner join #BOM_ProdVersion_Priority b  
        on a.component_cd = b.material_cd
    inner join #base_routes c
        on a.Demand_Plant_CD = c.Recipient_Plant_CD 
       and b.Plant_CD        = c.Source_Plant_CD
       and c.Material_CD     = b.Material_CD   -- Need to have material_cd to link
    where b.Material_CD != b.Component_CD
    and   b.Component_Quantity > 0      
    and   a.cycle_depth < 2 --stop the query if we start cycling, but only after we capture at least one full cycle
)

select *
into #BOMExploded
from BOM
WHERE cycle_flag IS NOT NULL
OPTION (MAXRECURSION 20)

这将捕获cycle_depth,它是一个计数器,用于衡量我们获得的循环深度。在我们得到cycle_depth为1后停止递归,这样循环就可以在最终选择中捕获。