In SQL Server
I have a database with a table called Recipe, this table contains recipes and the materials that go into the recipes. I need a query that shows all the base materials for a recipe. Unfortunately, some of these materials are actually other recipes within the recipes. FOR EXAMPLE: you have recipe AA01 and within that it makes up 2 base materials and recipe BB01. You must now look up the materials in that recipe to find the base materials in AA01. The trick is, you can have an infinite amount of recipes within recipes. This can be a ladder of searching. You don't know how far down the ladder you will have to look.
I came up with the idea of a query that searches and finds the next material and loops until it finally finds the base material. Sometimes it may have to loop once to find a material, other times it may have to loop 5 times to go down 5 levels.
The code below, unfortunately, I could not make it loop so it only finds the first layer. I could not make the query loop on itself.
SELECT
Recipe.RecipeID,
Recipe_1.RecipeID,
Recipe_1.MaterialID
FROM Recipe
LEFT JOIN Recipe AS Recipe_1 ON Recipe.MaterialID = Recipe_1.RecipeID
ORDER BY Recipe.RecipeID;
The solution would be to put this code in a loop or have it recursively call itself until it reaches a level where all base materials have been found. I have attached a picture of an example of a RECIPE table, what the code generates, and what I need it to generate (more levels) infinitely. You can see the highlighted rows are the ones missing from the actual one, those materials needed 2 levels to end up with the base materials. I have hard coded it to search up to 5 layers but obviously, there can be more. How can I have SQL loop and allocate the layers itself?
Is it possible to make a loop to continuously loop a query on itself?
This looks like it is producing the results you're looking for...
Results...
Edit: Below is the same solution as above but written to eliminate the use of the CONCAT function for earlier versions of SQL Server...
HTH, Jason
Easy :)
Level = 1
Create table variable with ingredients of top-level recipe (AA01) and mark records with
Level
(in separate column of your table variable)While count of obtained records was greater than zero
Increase Level
Fetch all ingredients which have parent recipe among the ingredients found in previous step (
Level - 1
) and add them to the temporary table variable.Loop
At this moment you have the result. Level in table also indicates depth of given ingredient.
This avoids recursion and is computationally very cheap.
Just make sure that you detect circular path during adding the ingredients in
AFTER INSERT
trigger (and fail adding if it was detected) so recipe BB01 in recipe AA01 does not have subrecipe AA01. But this is achievable by the above approach, too, so it is not a big problem.This code should do that (not tested, please report problems, if any):
Level
I usedRecipeDepth
, because the former one seems to be a reserved word or keyowrd;
because in the future they intended to make them mandatory