SQL BOM Tree Joining

SKU BOM SKU CLASS
RM123 D
WPR123 RM123 C
WPR456 RM123 C
123 WPR123 B
456 WPR456 A

Imagine that all of this is in a single table as pictured. What I want is to retrieve the class of SKU 123 and 456 starting with the raw RM123. This seems like a looping join that I just can't get my head around. Help please.


This sounds like you want a recursive CTE (some background here and here).

With this table and sample data:

CREATE TABLE dbo.ImagineThis
(
  SKU   varchar(32),
  BOM   varchar(32), 
  CLASS varchar(32)
);

INSERT dbo.ImagineThis(SKU,BOM,CLASS) VALUES
('RM123' , '',       'D'),
('WPR123', 'RM123' , 'C'),
('WPR456', 'RM123' , 'C'),
('123'   , 'WPR123', 'B'),
('456'   , 'WPR456', 'A');

One approach is:

;WITH cte AS
(
  SELECT lvl = 1, SKU, BOM, CLASS, Path = CONVERT(nvarchar(max), SKU)
  FROM dbo.ImagineThis
  WHERE BOM = ''
  
  UNION ALL
  
  SELECT lvl = lvl + 1, i.SKU, i.BOM, i.CLASS, Path = cte.Path + '\' + i.SKU
  FROM cte
  INNER JOIN dbo.ImagineThis AS i
  ON i.BOM = cte.SKU
)
SELECT * FROM cte OPTION (MAXRECURSION 32000);

Results:

lvl SKU BOM CLASS Path
1 RM123 D RM123
2 WPR123 RM123 C RM123\WPR123
2 WPR456 RM123 C RM123\WPR456
3 456 WPR456 A RM123\WPR456\456
3 123 WPR123 B RM123\WPR123\123

Though I'm not sure how you came to the answer "A" since these two paths seem "equal" and both A and B are at the same "level." You can see this by limiting the output to the highest level:

;WITH cte AS
(
  ...
)
SELECT TOP (1) WITH TIES *
  FROM cte 
  ORDER BY lvl DESC
  OPTION (MAXRECURSION 32000);

Why do you consider "A" as the final product?

  • Example db<>fiddle

As for why A

"A" (class) would be the final answer in terms of heirachary. A = 1, B = 2, etc,. I'd create either a temp table or CTE with this definition.

Ok:

DECLARE @ClassHierarchy table(Class varchar(32), Ordering tinyint);
INSERT @ClassHierarchy VALUES('A',1),('B',2),('C',3),('B',4);

;WITH cte AS
(
  SELECT lvl = 1, SKU, BOM, CLASS, Path = CONVERT(nvarchar(max), SKU)
  FROM dbo.ImagineThis
  WHERE BOM = ''
  
  UNION ALL
  
  SELECT lvl = lvl + 1, i.SKU, i.BOM, i.CLASS, Path = cte.Path + '\' + i.SKU
  FROM cte
  INNER JOIN dbo.ImagineThis AS i
  ON i.BOM = cte.SKU
)
SELECT TOP (1) WITH TIES cte.*
  FROM cte 
  INNER JOIN @ClassHierarchy AS h
  ON cte.CLASS = h.Class
  ORDER BY lvl DESC, h.Ordering
  OPTION (MAXRECURSION 32000);

Results:

lvl SKU BOM CLASS Path
3 456 WPR456 A RM123\WPR456\456
  • Updated db<>fiddle