搜索 parent-child 关系直到 parent 满足条件
Search parent-child relationship until parent meets a condition
我的 table 看起来像这样:
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
这个table包含了4个元素的child-parent关系。为了提供一些额外的细节,ChildType 指定零件是购买的、制造的还是由外部实体制造的。我有兴趣只购买与外部完成的 parent 相关的零件。然而,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)
我感兴趣的最终输出是一个 table,它将购买的零件(开头提供的一组零件)和它们的 parent 联系起来,只要它们是外部的,并绕过中间的任何幻影。
如果零件达到制造的 parent(任何不是外部或幻影的其他东西),那么它应该 return NULL 或一个标志表明这个 child 确实如此没有 Parent 外部制造。
我的意思是这样的:
ChildPart ExternalParent
-----------------------------
a0001 NULL
a0002 d0004
a0003 c0004
a0004 b0004
a0005 NULL
我一直在尝试为此使用 CTE,但还没有成功...
这是我的代码。我打算将每个 child 与其顶部的外部处理 parent 配对,然后 select 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
您在问题中的预期结果与示例数据不完全匹配。你最好修改一下,以免混淆。
我在递归查询中添加了一个明确的 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 |
+-----------+----------------+
我的 table 看起来像这样:
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
这个table包含了4个元素的child-parent关系。为了提供一些额外的细节,ChildType 指定零件是购买的、制造的还是由外部实体制造的。我有兴趣只购买与外部完成的 parent 相关的零件。然而,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)
我感兴趣的最终输出是一个 table,它将购买的零件(开头提供的一组零件)和它们的 parent 联系起来,只要它们是外部的,并绕过中间的任何幻影。
如果零件达到制造的 parent(任何不是外部或幻影的其他东西),那么它应该 return NULL 或一个标志表明这个 child 确实如此没有 Parent 外部制造。
我的意思是这样的:
ChildPart ExternalParent
-----------------------------
a0001 NULL
a0002 d0004
a0003 c0004
a0004 b0004
a0005 NULL
我一直在尝试为此使用 CTE,但还没有成功...
这是我的代码。我打算将每个 child 与其顶部的外部处理 parent 配对,然后 select 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
您在问题中的预期结果与示例数据不完全匹配。你最好修改一下,以免混淆。
我在递归查询中添加了一个明确的 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 |
+-----------+----------------+