SQL NOT IN解释

时间:2012-06-24 21:50:19

标签: sql

在尝试创建一个非常简单的SQL语句时,我发现自己已经迷失了。

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

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

它的存储方式使其更加清晰:

配方:

id | name
 1 | lasagne alla bolognese
 2 | tuna with tomatoes

成分:

id | name
 1 | lasagne slices
 2 | meat
 3 | tomato
 4 | tuna

和join-table ingredients_recipes:

ingredient_id | recipes_id
            1 | 1
            2 | 1
            3 | 1
            3 | 2
            4 | 2 

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

我希望我的所有食谱都有肉类番茄:

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).

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

2 个答案:

答案 0 :(得分: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
    );

更新:(我误读了这个问题)如果您还不想要使用特定成分 present 的食谱,您可以在查询中添加另一个子查询“leg”:

... AND NOT EXISTS ( SELECT *
    FROM ingredients_recipes ex
    WHERE ex.recipe_id = r.recipe_id
    AND ex.ingredient_id IN ( 4,5 )
    );

答案 1 :(得分:2)

尝试这种灵活的解决方案:

此查询将检索包含12成分的食谱,且不包含成分45

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列表中的ingredient_ids,并确保COUNT(*)中的值表示该列表中的项目数。它在这里做的是选择包含 成分1或2的食谱,这意味着一些食谱将只加入1行(如果它只包含一种成分而不是另一种),或者2行(如果它含有两种成分)。然后,HAVING COUNT(*) = 2仅选择加入 2 行的食谱。

选择这些食谱后,我们现在必须过滤掉包含成分45的食谱。我们使用LEFT JOIN / IS NULL执行此操作。与FROM子选择一样,您可以在IN列表中添加或删除成分,但不必担心调整COUNT中的LEFT JOIN