MySQL多对多联结表:选择A中的所有条目,其中B中没有值不在列表中

时间:2016-02-27 20:56:42

标签: mysql

我有多对多关系,通过联结表加入。我的具体案例是食谱和配料。我想选择所有不含给定列表中不含成分的食谱。例如,如果我输入奶酪,烤面包和饼干,我希望结果包括奶酪吐司,奶酪和薄脆饼干,但没有果酱吐司。

如下所示:

SELECT * FROM
recipe
JOIN recipe_ingredient on recipe.id = recipe_ingredient.recipe_id
JOIN ingredient on ingredient.id = recipe_ingredient.ingredient_id
WHERE ingredient.name
???
("cheese", "toast", "crackers")

选择含有任何或所有这些成分的配方很容易,但如果可以避免,我不希望随后过滤掉含有未列出成分的结果。

编辑:
一些示例表:

ingredient
-----------
id | name
1  | "cheese"
2  | "toast"
3  | "crackers"
4  | "jam"

recipe
-----------
id | name
1  | "cheese on toast"
2  | "cheese with crackers"
3  | "jam on toast"

recipe_ingredient
-------------------------
recipe_id | ingredient_id
1         | 1
1         | 2
2         | 1
2         | 3
3         | 2
3         | 4

2 个答案:

答案 0 :(得分:2)

实现此目的的一种方法是选择具有未在您的条件中列出的任何成分的配方,以使用ALL与子查询进行匹配:

SELECT r.id
FROM recipe r
JOIN recipe_ingredient ri on r.id = ri.recipe_id
JOIN ingredient i on i.id = ri.ingredient_id
WHERE i.name <> ALL ( SELECT 'cheese' UNION SELECT 'toast' UNION SELECT 'crackers' )
GROUP BY r.id

要仅检索符合条件的食谱,您可以使用完全相同的<> ALL比较来包装上述语句。

SELECT *
FROM recipe
WHERE id <> ALL (
    SELECT r.id
    FROM recipe r
    JOIN recipe_ingredient ri on r.id = ri.recipe_id
    JOIN ingredient i on i.id = ri.ingredient_id
    WHERE i.name <> ALL ( SELECT 'cheese' UNION SELECT 'toast' UNION SELECT 'crackers' )
    GROUP BY r.id
    );

附加说明:实际上NOT IN<> ALL的别名,因此您可以互换使用它们。

鉴于您的样本,它只会返回:

id | name
---|-------------------------
1  | cheese on toast
2  | cheese with crackers

在此处查看:http://sqlfiddle.com/#!9/f20010/25

答案 1 :(得分:0)

这个很好玩。但这是可以完成的。这里的技巧部分是知道每种配方含有多少成分,然后将其与给定参数的成分含量进行比较。

ALTER TABLE links_chatpicmessage
  ADD FOREIGN KEY (sender)
  REFERENCES auth_user;

首先,我选择了所有含有这些成分的食谱,从中计算出它含有多少成分。第一个查询会给我:

select tb.name
  from ( select r.id, r.name, count(*) qtd
           from ingredient i
                  inner join recipe_ingredient ri 
                     on i.id = ri.ingredient_id
                  inner join recipe r
                     on r.id = ri.recipe_id
           where i.name in ('cheese', 'toast', 'crackers')
           group by r.id, r.name
        ) tb
  where exists ( select 1
                   from ingredient i
                         inner join recipe_ingredient ri 
                            on i.id = ri.ingredient_id
                         inner join recipe rr
                            on rr.id = ri.recipe_id
                  where rr.id = tb.id
                  group by rr.id
                  having count(*) = tb.qtd)

"cheese on toast" 2 "cheese with crackers" 2 "jam on toast" 1 条款中,我创建了一个子查询来计算所有食谱的总成分并与上子查询结合。所以它只会给我列出的那些。

在此处查看:http://sqlfiddle.com/#!9/f20010/22