Reputation: 1035
I'm using PostgreSQL. I have a table with 3 fields person, recipe and ingredient
person = creator of the recipe
recipe = the recipe
ingredient = one of the ingredients in the recipe
I want to create a query which results in every person who whenever has added carrot to a recipe, the person must also have added salt to the same recipe.
More than one person can have created the recipe, in which case the person who added the ingredient will be credited for adding the ingredient. Sometimes the ingredient is used more than once, even by the same person.
If this the table:
person1, rec1, carrot
person1, rec1, salt
person1, rec1, salt
person1, rec2, salt
person1, rec2, pepper
person2, rec1, carrot
person2, rec1, salt
person2, rec2, carrot
person2, rec2, pepper
person3, rec1, sugar
person3, rec1, carrot
Then I want this result: person1
Because this person is the only one who whenever has added carrot also have added salt.
"Nothing but the carrot could affect the result. I only want persons who has added at least one carrot in one of their recipes, but I don't want persons who have not also added salt to all of the same recipes they've added carrot to. Sorry, but I just can't explain it any clearer than that."
Upvotes: 4
Views: 211
Reputation: 115520
This seems a variation of the relational division problem.
A double-nested NOT EXISTS
solution:
SELECT DISTINCT person
FROM tableName AS t
WHERE NOT EXISTS
( SELECT *
FROM tableName AS chosen
WHERE chosen.ingredient = 'carrot'
AND chosen.person = t.person
AND NOT EXISTS
( SELECT *
FROM tableName AS required
WHERE required.ingredient = 'salt'
AND required.recipe = chosen.recipe
AND required.person = chosen.person
)
) ;
And a JOIN
:
SELECT DISTINCT
t.person
FROM
tableName AS t
LEFT JOIN
tableName AS chosen
LEFT JOIN
tableName AS required
ON required.ingredient = 'salt'
AND required.recipe = chosen.recipe
AND required.person = chosen.person
ON chosen.ingredient = 'carrot'
AND chosen.person = t.person
AND required.ingredient IS NULL
WHERE
chosen.ingredient IS NULL ;
Upvotes: 0
Reputation: 106385
How about this:
SELECT DISTINCT person
FROM tableName
WHERE ingredient IN('carrot', 'salt')
GROUP BY person, recipe
HAVING SUM(CASE WHEN ingredient = 'carrot' THEN 1 ELSE -1 END) <= 0
AND
COUNT(DISTINCT ingredient) > 1;
I admit I don't have much experience working with PostgreSql, but the query seems to give the results you require in this SQL Fiddle (credit to @JohnWoo for providing the one to begin from).
I have updated the answer; before it returned users with only salts
in some of their recipes as legit ones. The second HAVING
clause filters out such cases.
UPDATE: The previous query returned all the owners which have at least one recipe which follows the rule ("for each added carrot add salt also"). But you (seem to) actually need the ones with all the recipes following the rule. So the query looks like...
SELECT DISTINCT person
FROM tableName
WHERE person NOT IN (
SELECT person
FROM tableName
WHERE ingredient IN('carrot', 'salt')
GROUP BY person, recipe
HAVING SUM(CASE WHEN ingredient = 'carrot' THEN 1 ELSE -1 END) > 0
);
SQL Fiddle to play with.
Upvotes: 1
Reputation:
Try:
SELECT person from
(SELECT person, recipe, COUNT(DISTINCT ingredient) ingredients
FROM tableName
WHERE ingredient IN ('salt', 'carrot')
GROUP BY person, recipe
HAVING MAX(CASE WHEN ingredient = 'carrot' THEN 1 END) = 1) p
group by person
HAVING MIN(ingredients) = 2
Upvotes: 0