I'm struggling to wrap my head around this one. I'm sure someone will simplify it.
We have a parent/child relationship that I'm needing to roll-up based on sold quantities and sales dollar amounts.
We have a table that stores the parent and their children. Looks like this:
Parent Component
1000 1300
1000 1301
When an order is placed, our users order a part number of 1000, then add in line items 1300, 1301 as options. Parent parts do not have a sales value assigned to them, but components do have a value. So what I'm trying to do is roll up everything to the parent level (sales and quantities) on a sales order.
Here's how a sales order would look:
SalesOrder StockCode ParentChild Qty Price
0001 1000 P 1 0
0001 1300 C 1 500
0001 1301 C 1 350
0001 1301 1 400
As you can see above, we can have components (StockCode
1301) in here without parents (think of these as replacement parts). If the sales order line does not have a ParentChild
value, they need to be excluded from the roll-up.
Here's how I'm wanting this data to look. A parent/child should only have a Qty of the parent.
SalesOrder StockCode ParentChild Qty Price
0001 1000 P 1 850
0001 1301 1 400
I'm guessing that STUFF/FOR XML PATH would be a good starting point?
Here is the test data:
CREATE TABLE #Structure (
Parent INT
,Component INT )
INSERT INTO #Structure (Parent, Component)
SELECT 1000, 1300
UNION
SELECT 1000, 1301
CREATE TABLE #SalesOrder (
SalesOrder VARCHAR(4)
,StockCode INT
,ParentChild CHAR(1)
,Qty INT
,Price INT )
INSERT INTO #SalesOrder (SalesOrder, StockCode, ParentChild, Qty, Price)
SELECT '0001', 1000, 'P', 1, 0
UNION
SELECT '0001', 1300, 'C', 1, 500
UNION
SELECT '0001', 1301, 'C', 1, 350
UNION
SELECT '0001', 1301, NULL, 1, 400
SELECT * FROM #Structure
SELECT * FROM #SalesOrder
DROP TABLE #SalesOrder
DROP TABLE #Structure
Best Answer
TLDR: Final query with comments is at the end, but the following answer goes through the steps of building it out.
So what you likely want is a recursive CTE (I'm not sure STUFF can help you with this, at least not easily).
You should have an Item table that is a distinct list of all your assemblies and component item IDs, similar to this example:
If I recall correctly from your last question, you have a BOM table that should have a similar structure as this example (with other fields too I'm sure):
A recursive CTE will help you flatten this so all child Components at all levels roll up with the main end item (e.g. Component 2003 rolls up with 2001 into the end item parent 1000).
Here's an example of a recursive CTE:
In my example dataset, this produces the following output (every level of every component rolled back up to the original end item):
Notice end item "1000" on rows 16 through 18. Rows 16 and 17 are "level 1" and row 18 is "level 2" because the component "2004" on row 17 has a component below it, therefor it's the parent on row 18 to component "2005".
Now let's bring in the Component Price column so we can later group by End Item and sum up the total cost like so:
Finally, if you add a GROUP BY on the End Item column and add a SUM(ComponentPrice) column then you can get the total exploded BOM price of the End Item like so:
End Items' Total Prices:
TLDR:
Your final step would be to join these rolled up End Item prices to your Sales Line table by ItemId to EndItemId like so:
Sales Lines w/ their summed up component prices:
Your table schema will obviously vary from my example, but this is the concept and example code of what you'd need to do. Instead of ItemIds it looks like you have StockCodes (or whatever your item's identifier is called in your Item and BOM tables).
The only other important thing to be aware of is if you have a BOM that is more than 2 levels deep (e.g. Parent -> Child -> Grandchild -> Great Grandchild) then you'll only want to sum up the lowest level component items otherwise you end up double counting. E.g. if Item A has component B, and component B has components C and D on it, then you want to only sum the prices of C and D for A. If you sum it all then you'll end up double counting by adding B + C + D for A's price. In my past experience we had a column called ItemType that identified which items are the lowest level component items so that when we did the roll up we could add a WHERE ItemType = 'LowestLevelComponent' but it sounds like in your case, all items that have a child don't have a price so then you probably don't have to worry about double counting.
Finding the total quantity of the lowest level components is the same idea too, but instead you'll need a column in the recursive CTE for the ParentQuantity and another one that multiplies the ParentQuantity by the Child's quantity (in the recursive part of the query) like so:
If you have any questions feel free to let me know and I'll explain as best as I can. If this feels like information overload, maybe just start with the TLDR final full query above and just read the comments to try to understand what it does.