2

在尝试创建一个实际上非常简单的 SQL 语句时,我发现自己迷失了方向。

我有一个包含 3 个表的数据库:

  • 食谱 - 存储一些用于烹饪的食谱名称
  • 配料食谱 - 将配料与食谱联系起来
  • 成分 - 食谱使用的成分。

它像这样存储以使其双重清晰:

食谱:

编号 | 姓名
 1 | 烤宽面条
 2 | 金枪鱼配西红柿

配料:

编号 | 姓名
 1 | 千层面
 2 | 肉
 3 | 番茄
 4 | 金枪鱼

并加入表成分_食谱:

成分ID | recipes_id
            1 | 1
            2 | 1
            3 | 1
            3 | 2
            4 | 2

正如你所看到的,有 2 种非常美味的食谱,我至少应该给它们一些香料。但我之前想做的是按成分选择食谱。

我想让我所有的食谱都有肉西红柿:

SELECT recipes.name FROM recipes r
INNER JOIN ingredients_recipes ir ON ir.recipes_id = r.id
WHERE ir.ingredient_id IN ( 2 ) AND ir.ingredient_id IN ( 3 )

-> 烤宽面条.. 好!(我使用 IN 因为可能有一堆成分,比如“西红柿”、“西红柿”、“西红柿、切片”等。)

例如,当我想要所有的食谱都有西红柿但没有金枪鱼时,我尝试了:

SELECT recipes.name FROM recipes r
INNER JOIN ingredients_recipes ir ON ir.recipes_id = r.id
WHERE ir.ingredient_id IN ( 2 ) AND ir.ingredient_id NOT IN ( 4 )

-> 仍然得到金枪鱼 - 因为我加入的行之一不包含成分 4。好的:/

我现在想知道的是,我必须做什么才能得到我想要的结果。我目前通过这样的子选择在我的膝盖上放了一个箭头:

SELECT recipes.name FROM recipes r
INNER JOIN ingredients_recipes ir ON ir.recipes_id = r.id
WHERE (
        ir.ingredient_id IN ( 2 ) -- or more..
        AND 
        recipes.id NOT IN ( SELECT recipes_id FROM ingredients_recipes
                             WHERE ingredient_id IN ( 4 ) -- actually i paste names of the ingredients.. but that is not the case. just to shorten the query.. by filling in a comment twice as long..
                            )
      )

因为我对 mySQL 很陌生,所以我什至不知道要谷歌搜索什么。所以在这种情况下的任何帮助,并且o/c更好的SQL语句将是..

 IN(awesome).

编辑:啊,是的,我实际上是在分组.. ;-)

4

2 回答 2

2

这是一种方式。(未经测试)

SELECT r.name 
FROM recipes r
JOIN ingredients_recipes ir 
  ON ir.recipe_id = r.id
WHERE EXISTS ( SELECT *
    FROM ingredients_recipes ex
    WHERE ex.recipe_id = r.recipe_id
    AND ex.ingredient_id IN ( 2 , 3 )
    GROUP BY nx.recipe_id
    HAVING COUNT(*) = 2
    );

count(*) 为 2 的唯一方法是如果 2 和 3 都存在。

另一种方法可能是:(仍未测试)

SELECT r.name 
FROM recipes r
JOIN ingredients_recipes ir 
  ON ir.recipe_id = r.id
WHERE EXISTS ( SELECT *
    FROM ingredients_recipes ex
    WHERE ex.recipe_id = r.recipe_id
    AND ex.ingredient_id = 2
    )
AND EXISTS ( SELECT *
    FROM ingredients_recipes ex
    WHERE ex.recipe_id = r.recipe_id
    AND ex.ingredient_id = 3
    );

更新:(我误读了这个问题)如果您也不希望出现具有特定成分的食谱,可以在查询中添加另一个子查询“leg”:

... AND NOT EXISTS ( SELECT *
    FROM ingredients_recipes ex
    WHERE ex.recipe_id = r.recipe_id
    AND ex.ingredient_id IN ( 4,5 )
    );
于 2012-06-24T22:01:43.787 回答
2

试试这个灵活的解决方案:

此查询将检索同时包含成分1and2且不包含成分4and5的食谱:

SELECT
    a.*
FROM 
    (
        SELECT aa.id, aa.name
        FROM recipes aa
        INNER JOIN ingredients_recipes bb ON aa.id = bb.recipe_id
        WHERE bb.ingredient_id IN (1,2) --Ingredients to contain
        GROUP BY aa.id, aa.name
        HAVING COUNT(*) = 2 --Count of items in containing list
    ) a
LEFT JOIN
    ingredients_recipes b ON 
        a.id = b.recipe_id AND
        b.ingredient_id IN (4,5) --Ingredients to NOT contain
WHERE 
    b.recipe_id IS NULL

解释:

FROM子选择获取所有同时包含成分12. 如果您希望添加或删除配方中包含的成分,只需调整IN列表中的成分 ID,并确保中的值COUNT(*)代表该列表中的项目数。它在这里所做的是选择包含成分 12 的食谱,这意味着某些食谱将仅加入 1 行(如果它只包含一种成分但不包含另一种成分)或 2 行(如果它包含两种成分)。然后HAVING COUNT(*) = 2只选择加入2行的食谱。

一旦选择了这些食谱,我们现在必须过滤掉包含成分45. 我们用LEFT JOIN/来做到这一点IS NULL。就像在子选择中一样FROM,您可以在列表中添加或删除成分IN,除了您不必担心调整COUNT.LEFT JOIN

于 2012-06-24T22:15:35.613 回答