Reputation: 23
For starters, some background information. I am building a simple recipe book database and I'm trying to build a query that will show me what I can make based on the items in my pantry. The table schemas are as follows:
RECIPE(*RecipeID*, RecipeName)
RECIPE_INGREDIENT(***RecipeID***, **IngredientID**)
INGREDIENT(*IngredientID*, IngredientName)
PANTRY_ITEM(*ItemID*, ItemName)
The fields in the RECIPE_INGREDIENT table make up a composite key and are both foreign keys into the RECIPE(RecipeID) and INGREDIENT(IngredientID) table. I'm using the following test data:
RECIPE table
RecipeID,RecipeName
1,'food 1'
2,'food 2'
INGREDIENT Table
IngredientID, IngredientName
1,'ing 1'
2,'ing 2'
3,'ing 3'
RECIPE_INGREDIENT table
RecipeID,IngredientID
1,1
1,2
2,2
2,3
PANTRY_ITEM table
ItemID,ItemName
1,'ing 2'
2,'ing 3'
So essentially I'm trying to query a list of RecipeNames based on the items I have in my pantry. By this, I mean that I must have all the ingredients for the recipe in my pantry for it to be added to the list. Therefore the ideal query based on this test data would result in only 'food 2'. The problem I'm running into is enforcing the 'all ingredients' for a recipe part.
I've tried a few different queries and they all result in every recipe being returned.
SELECT RecipeName FROM RECIPE WHERE RecipeID IN (SELECT RecipeID FROM RECIPE_INGREDIENT WHERE IngredientID IN (SELECT IngredientID FROM INGREDIENT WHERE IngredientName IN (SELECT ItemName FROM PANTRY_ITEM)))
Does anyone have any ideas as to how I can accomplish this? Is there a possible query for this, or would I have to restructure my database?
Upvotes: 0
Views: 308
Reputation: 5636
Actually, I don't see why you need more than the two tables. Your Recipes table needs two more fields. A unit of measurement (tsp, cup, oz, etc.) and how many of that measurement is needed for the recipe. The Ingredients table needs the same two fields only this is the amount available in the pantry. You need the other unit of measurement because a recipe may call for a cup of sugar but you buy sugar by the pound.
You'll also need a udf to convert from one unit to another so if the recipe calls for 2 tsp vanilla and the pantry shows 1 oz left, is that enough?
Then just perform an outer join between recipes and ingredients and ignore those with any null ingredients (no or not enough of an ingredient in the pantry).
Upvotes: 0
Reputation: 8751
Here is your answer
select r1.recipeName from recipe r1 where r1.recipeName not in
(
select r.recipeName from recipe r
inner join
recipe_ingredient ri
on r.recipeid=ri.RecipeID
inner JOIN
ingredient i
on ri.IngredientID=i.ingredientid and
i.IngredientName not in (select itemname from pantry_item) )
Upvotes: 0
Reputation: 145
This WILL NOT perform well, but it's possible without changing your schema. I'd recommend though, perhaps you should consider having your PANTRY table id's match the ingredient id's...
Essentially, we have to find any time a NULL value is presented when comparing a recipe ingredient to it's pantry item equivalent. Any instance of NULL in the sub query means there is a missing item... So we just generate an exclusion list and compare the results to that.
SELECT RecipeID AS _id, RecipeName FROM RECIPE WHERE RecipeID NOT IN (
SELECT d.RecipeId FROM (
SELECT p.ItemID, r.RecipeID FROM PANTRY_ITEM p
JOIN INGREDIENT i ON (p.ItemName = i.IngredientName)
RIGHT JOIN RECIPE_INGREDIENT ri ON (ri.IngredientId = i.IngredientId)
RIGHT JOIN RECIPE r ON (r.RecipeId = ri.RecipeId)
) d WHERE d.ItemID IS NULL
);
Upvotes: 0
Reputation: 92805
First off if you can put only ingredients into the pantry your pantry
schema should rather look like this
CREATE TABLE pantry
(
IngredientID int,
FOREIGN KEY (IngredientID) REFERENCES ingredient (IngredientID)
);
Now, you can leverage HAVING
clause to get the desired result
SELECT recipename
FROM
(
SELECT recipeid
FROM recipe_ingredient ri LEFT JOIN pantry p
ON ri.ingredientid = p.ingredientid
GROUP BY recipeid
HAVING COUNT(*) = COUNT(p.ingredientid)
) q JOIN recipe r
ON q.recipeid = r.recipeid
Output:
| RecipeName | |------------| | food 2 |
Here is a SQLFiddle demo
Upvotes: 1