views:

56

answers:

1

Let's pretend I have a large recipe-database. A table for recipes each with an unique ID and a table for ingredients with a whole lot of sets like this:

ID | RECIPE    |  INGREDIENT
-------------------------------
1  | recipe_a  |  ingredient_a
2  | recipe_a  |  ingredient_b
3  | recipe_a  |  ingredient_c
4  | recipe_b  |  ingredient_a
5  | recipe_b  |  ingredient_d
6  | recipe_b  |  ingredient_e

Users can search for ingredients they want to see in their recipes and those they don't. The wanted query should be able to result in recipe_a when a user searches for recipes with ingredient_a and ingredient_b but not ingredient_d.

How would one do that in preferrably one query?

I tried the rather naive version with:

SELECT distinct recipe 
  from ingredients 
 where ingredient in (ingredient_a, ingredient_b) 
   and ingredient not in (ingredient_d)

This obv failed, because it still resulted in recipe_a and recipe_b, which it should do, because the rows 1 and 2 matched recipe_a and row 4 matched recipe_b.

+2  A: 
Select Distinct ...
From Recipes As R
Where R.ingredient in(ingredient_a, ingredient_b...)
    And Not Exists(
                    Select 1
                    From Recipes As R2
                    Where R2.Recipe = R.Recipe
                        And R2.Ingredient In(ingredient_d)
                    )

As Jeffrey L Whitledge mentioned, the above query will return any recipe that has at least one ingredient in the desired list and none in the undesired list. However, if you wanted to return recipes that contained all the ingredients in the desired list and none in the undesired list you could do:

Select Distinct ...
From Recipes As R
Where Exists    (
                Select 1
                From Recipes As R2
                Where R2.Recipe = R.Recipe
                    And R2.ingredient in(ingredient_a, ingredient_b...)
                Having Count(*) = @CountOfPassedIngredients
                )
    And Not Exists(
                    Select 1
                    From Recipes As R2
                    Where R2.Recipe = R.Recipe
                        And R2.Ingredient In(ingredient_d)
                    )

In this scenario, you would need to have first determine the count of desired ingredients.

Thomas
Unless I'm missing something, this reads as though it returns the recipes that include ANY rather than ALL of the desired ingredients.
Jeffrey L Whitledge
That's a question for the OP. Is the desired result to return recipes that contain all of a set of given ingredients and none of the undesired ones or should it return any recipe that has at least one of the given ingredients and none of the undesired ones?
Thomas
Both versions work like a charm and will let me add this, so that the user can choose the mode. Greatly appreciated!
KMB