Reputation: 101
I am trying to create a query that returns a specific set of registers, but i tried several ways and it is not working.
I have 4 tables like below:
recipe {
idRecipe integer,
name varchar(100)
}
recipeStep {
idStep integer,
idRecipe integer,
instruction varchar(100)
}
recipeIngredient {
idStep integer,
idIngredient integer
}
ingredient {
idIngredient integer,
name varchar(100)
}
The filter is to return all recipe that contain the ingredients selected by the user. Lets say i want all recipes that contains all recipes connotation the ingredients 68,25,36,109. So I came to this:
SELECT r.*
FROM recipe r, recipeStep rs, recipeIngredient ri
WHERE r.idRecipe = rs.idRecipe
AND rs.idStep = ri.idStep
AND ri.idIngredient in (68,25,36,109)
The problem is that this query returns ALL recipes that contains ANY of those ingredients and I want only ALL recipes that contains ALL of them
Upvotes: 0
Views: 85
Reputation: 44901
One way to do this is to use a group by
clause together with a having
clause that limits the groups to the ones that have a count of 4 distinct ingredients.
SELECT r.idRecipe, r.name
FROM recipe r
JOIN recipeStep rs ON r.idRecipe = rs.idRecipe
JOIN recipeIngredient ri ON rs.idStep = ri.idStep
WHERE ri.idIngredient IN (68,25,36,109)
GROUP BY r.idRecipe, r.name
HAVING COUNT(DISTINCT ri.idIngredient) = 4;
Alternatively you could skip the IN predicate and use conditional aggregation in the having
clause:
SELECT r.idRecipe, r.name
FROM recipe r
JOIN recipeStep rs ON r.idRecipe = rs.idRecipe
JOIN recipeIngredient ri ON rs.idStep = ri.idStep
GROUP BY r.idRecipe, r.name
HAVING SUM(CASE WHEN ri.idIngredient = 68 THEN 1 END) > 0
AND SUM(CASE WHEN ri.idIngredient = 25 THEN 1 END) > 0
AND SUM(CASE WHEN ri.idIngredient = 36 THEN 1 END) > 0
AND SUM(CASE WHEN ri.idIngredient = 109 THEN 1 END) > 0;
Both queries would allow additional ingredients, as long as the recipe has the four mandatory ones. If you want exact matches you can just add another condition to the where clause.
Upvotes: 2