【问题标题】:Search parent-child relationship until parent meets a condition搜索父子关系,直到父满足条件
【发布时间】:2016-05-04 04:01:50
【问题描述】:

我的桌子是这样的:

ChildPart    ParentPart    Quantity    ChildType
--------------------------------------------------
a0001        b0001         1           Bought
a0002        b0002         1           Bought
a0003        b0003         1           Bought
a0004        b0004         1           Bought
a0005        x0000         1           Made
b0001        c0001         1           Phantom
b0002        c0002         1           Phantom
b0003        x0000         1           External
b0004        c0004         1           Phantom
c0001        d0001         1           Phantom
c0002        x0000         1           External
c0004        d0004         1           Phantom
d0001        x0000         1           Made
e0004        x0000         1           External
x0000        x0000         1           Made

此表包含 4 个元素的子父关系。为了提供一些额外的细节,ChildType 指定零件是由外部实体购买、制造还是制造。我有兴趣只获得与父母相关的在外部完成的购买部分。但是,应该忽略 Phantom 状态,因为它是一个假零件,仅用于跟踪最小零件转换。

每个部分的过程更好的说明如下:

part  a0001 -> b0001 -> c0001 -> d0001 -> x0000
type  Bought - Phanto - Phanto - Made   - Final Assembly (Made)

part  a0002 -> b0002 -> x0002 -> d0000
type  Bought - Phanto - Extern - Final Assembly (Made)

part  a0003 -> b0001 -> x0000  
type  Bought - Extern - Final Assembly (Made)

part  a0004 -> b0004 -> c0004 -> d0004 -> e0004 -> x0000
type  Bought - Phanto - Phanto - Phanto - Extern - Final Assembly (Made)

part  a0005 -> x0000
type  Bought - Final Assembly (Made)

我感兴趣的最终输出是一个表格,它将购买的零件(开头提供的一组零件)及其父母联系起来,只要它们是外部的,并且绕过中间的任何 Phantom。

如果该部分到达了一个被制造的父级(任何其他不是外部或幻象的),那么它应该返回 NULL 或一个标志,表明该子级没有外部制造的父级。

我的意思是这样的:

ChildPart    ExternalParent
-----------------------------
a0001        NULL
a0002        d0004        
a0003        c0004        
a0004        b0004        
a0005        NULL

我一直在尝试为此使用 CTE,但还没有任何运气......

这是我的代码。我打算将每个孩子与他们的顶级外部处理父母配对,然后选择 MainChild 和 ExternalParent 列。

    DECLARE @BOM TABLE(
    ChildPart VARCHAR(20)
    ParentPart VARCHAR(20)
    Quantity DEC(9,2)
    ChildType VARCHAR(20)
    )
    INSERT INTO @BOM VALUES
    ('a0001','b0001',1,'Bought')
    ,('a0002','b0002',1,'Bought')
    ,('a0003','b0003',1,'Bought')
    ,('a0004','b0004',1,'Bought')
    ,('a0005','b0005',1,'Made')
    ,('b0001','c0001',1,'Phantom')
    ,('b0002','c0002',1,'Phantom')
    ,('b0003','c0003',1,'External')
    ,('b0004','c0004',1,'Phantom')
    ,('c0001','d0001',1,'Phantom')
    ,('c0002','d0002',1,'External')
    ,('c0004','d0004',1,'Phantom')
    ,('d0001','e0001',1,'Made')
    ,('e0004','f0004',1,'External')
    ;
    DECLARE @partsToLook TABLE (ChildPart VARCHAR (20)
    INSERT INTO @partsToLook VALUES ('a0001'),('a0002'),('a0003'),('a0004'),('a0005')

    ----
    ;WITH cte AS 
    (
        SELECT  
            MainPart = p.ChildPart --This is to track the Main Child part we are looking the parents.
            ,ChildPart
            ,ParentPart
            ,Quantity
            ,ChildType
        FROM    @BOM b
        INNER JOIN @partsToLook p ON p.ChildPart=b.ChildPart

        UNION ALL

        SELECT  
            MainPart = tb.ChildPart
            ,ChildPart
            ,ParentPart
            ,Quantity
            ,ChildType
        FROM cte tb
        INNER JOIN @BOM b ON b.ChildPart=tb.ParentPart
    )
    SELECT MainPart,ParentPart FROM cte

【问题讨论】:

    标签: sql sql-server common-table-expression hierarchical-data recursive-query


    【解决方案1】:

    您在问题中的预期结果与示例数据不完全匹配。您最好修复它以避免混淆。

    我在递归查询中添加了一个显式的StopRecursion 标志,当递归到达不是'Bought', 'Phantom', 'External' 的行时设置该标志。

    然后ROW_NUMBER 用于每个StartPart 只选择一行,StopRecursion 标志用于确定是否应将ExternalParent 设置为NULL

    样本数据

    DECLARE @BOM TABLE(
        ChildPart VARCHAR(20)
        ,ParentPart VARCHAR(20)
        ,Quantity DEC(9,2)
        ,ChildType VARCHAR(20)
    );
    
    INSERT INTO @BOM (ChildPart,ParentPart,Quantity,ChildType) VALUES
     ('a0001','b0001',1,'Bought')
    ,('a0002','b0002',1,'Bought')
    ,('a0003','b0003',1,'Bought')
    ,('a0004','b0004',1,'Bought')
    ,('a0005','b0005',1,'Made')
    ,('b0001','c0001',1,'Phantom')
    ,('b0002','c0002',1,'Phantom')
    ,('b0003','c0003',1,'External')
    ,('b0004','c0004',1,'Phantom')
    ,('c0001','d0001',1,'Phantom')
    ,('c0002','d0002',1,'External')
    ,('c0004','d0004',1,'Phantom')
    ,('d0001','e0001',1,'Made')
    ,('e0004','f0004',1,'External')
    ;
    
    DECLARE @partsToLook TABLE (ChildPart VARCHAR (20));
    INSERT INTO @partsToLook (ChildPart) VALUES
    ('a0001'),
    ('a0002'),
    ('a0003'),
    ('a0004'),
    ('a0005');
    

    查询

    WITH
    CTE
    AS
    (
        SELECT
            B.ChildPart
            ,B.ParentPart
            ,B.ChildType
            ,1 AS Lvl
            ,B.ChildPart AS StartPart
            ,CASE WHEN B.ChildType NOT IN ('Bought', 'Phantom', 'External') 
                THEN 1 ELSE 0 END AS StopRecursion
        FROM
            @BOM AS B
            INNER JOIN @partsToLook AS P ON P.ChildPart = B.ChildPart
    
        UNION ALL
    
        SELECT
            B.ChildPart
            ,B.ParentPart
            ,B.ChildType
            ,CTE.Lvl + 1 AS Lvl
            ,CTE.StartPart
            ,CASE WHEN B.ChildType NOT IN ('Bought', 'Phantom', 'External') 
                THEN 1 ELSE 0 END AS StopRecursion
        FROM
            @BOM AS B
            INNER JOIN CTE ON CTE.ParentPart = B.ChildPart
        WHERE
            CTE.StopRecursion = 0
    )
    ,CTE_RN
    AS
    (
        SELECT
            StartPart
            ,ParentPart
            ,StopRecursion
            ,ROW_NUMBER() OVER (PARTITION BY StartPart ORDER BY Lvl DESC) AS rn
        FROM CTE
    )
    SELECT
        StartPart AS ChildPart
        ,CASE WHEN StopRecursion = 1 THEN NULL ELSE ParentPart END AS ExternalParent
    FROM CTE_RN
    WHERE rn = 1
    ORDER BY ChildPart;
    

    结果

    +-----------+----------------+
    | ChildPart | ExternalParent |
    +-----------+----------------+
    | a0001     | NULL           |
    | a0002     | d0002          |
    | a0003     | c0003          |
    | a0004     | d0004          |
    | a0005     | NULL           |
    +-----------+----------------+
    

    【讨论】:

    • 我还没有详细阅读您的答案,但一个观察结果是 bom 中的所有部分都是“购买”、“幻影”或“外部”。我已更新 BOM 以更好地解释我的情况。对困惑感到抱歉。我会在详细阅读您的解释后立即回复您。谢谢。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-02-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-11-10
    相关资源
    最近更新 更多