难以理解的查询行为

时间:2015-05-15 18:34:43

标签: mysql sql database

我有多个表,由多个外键相关,如下例所示:

  • and - Recipes(id_recipe,name,calories,category)为PK。
  • id_recipe - Ingredients(id_ingredient,name,type)为PK。
  • id_ingredient - Contains(id_ingredient,id_recipe,quantity,unit)为PK,以及(id_ingredient,id_recipe)Recipes(id_recipe)的外键。

您可以在此图片中看到此关系。 enter image description here

所以基本上Ingredients(id_ingredient)ContainsRecipes之间的桥梁。

我试图写它的查询应该给出其成分类型为“牛”但不是“乳酸”的食谱的名称。

我的尝试:

Ingredients

问题是它仍然向我展示了至少含有一种乳酸成分的食谱。

我将不胜感激任何帮助!

5 个答案:

答案 0 :(得分:1)

这是您需要的查询类型的一般形式:

SELECT *
FROM tableA
WHERE tableA.ID NOT IN (
   SELECT table_ID
   FROM ...
)
;

- 以下示例 -

子查询给出了" lactic"的所有食谱的id值。使用成分,外部查询说"给我所有不在该列表中的食谱"。

SELECT DISTINCT Recipes.name
FROM Recipes
WHERE id_recipe IN (
   SELECT DISTINCT id_recipe 
   FROM `Ingredients` AS `i`
      INNER JOIN `Contains` AS `c` USING (id_ingredient)
   WHERE `i`.`type` = "lactic"
)
;

或者,使用原始查询: 您可以将第二次加入更改为LEFT JOIN,将其USING更改为ON&相反,包含AND type = "lactic",并以HAVING Ingredients.type IS NULL结束查询(或者WHERE,我只是喜欢HAVING for"最终结果"过滤)。这会告诉你哪些物品无法加入"乳酸"成分

答案 1 :(得分:1)

此类问题的常见解决方案(检查一组行的条件)使用聚合+ CASE。

SELECT R.Name
FROM Recipes R
INNER JOIN Contains C
 on R.ID_Recipe = C.ID_Recipe
INNER JOIN Ingredients I
 on C.ID_Ingredient = I.ID_Ingredient
GROUP BY R.name
having -- at least one 'lactic' ingredient
  sum(case when type = 'lactic' then 1 else 0 end) = 0
and    -- no 'bovin' ingredient
  sum(case when type = 'bovin' then 1 else 0 end) > 0

很容易扩展到任何数量的成分和任何类型的问题。

劫持了xQbert的fiddle

答案 2 :(得分:0)

SELECT R.NAME
FROM CONTAINS C
INNER JOIN INGREDIENTS I
ON I.ID_INGREDIENTS = C.ID_INGREDIENTS AND I.TYPE = 'bovine' AND I.TYPE <> "lactic"
INNER JOIN RECIPES R 
ON R.ID_RECIPE = C.ID_RECIPE
GROUP BY R.NAME 

这应该有用,也许你需要逃避'包含'。它可以被识别为SQL函数。

答案 3 :(得分:0)

SQL Fiddle  在我的例子中汉堡和意大利面有&#39; Bovin&#39;从而出现。饼干也是如此,但饼干也有“乳酸”。这就是他们被排除在外的原因。

SELECT R.Name
FROM Recipes R
INNER JOIN Contains C
 on R.ID_Recipe = C.ID_Recipe
INNER JOIN Ingredients I
 on C.ID_Ingredient = I.ID_Ingredient
LEFT JOIN (SELECT R2.ID_Recipe
           FROM Ingredients I2
           INNER JOIN Contains C2 
             on C2.ID_Ingredient = I2.ID_Ingredient
           INNER JOIN Recipes R2               
             on R2.ID_Recipe = C2.ID_Recipe
           WHERE Type = 'lactic'
           GROUP BY R2.ID_Recipe) T3
 on T3.ID_Recipe = R.ID_Recipe
WHERE T3.ID_Recipe is null
 and I.Type = 'Bovin'
GROUP BY R.name

可能有更优雅的方式来做到这一点。我真的想CTE这个加入它自己..但在mySQL中没有CTE。可能使用存在的方法也是如此....我不是使用IN子句的忠实粉丝,因为性能通常会受到影响。存在最快,加速第二快,最慢(一般来说)

内联视图(子查询)返回您不想包含的ID_recipe。

外部查询返回所需配料的食谱名称。

通过使用外部连接将这两者连接在一起,我们返回所有配方,只返回含有不需要成分的配方。然后,我们将结果限制为仅针对不需要的成分不存在配方ID的结果。 (没有找到不需要的成分)你只能得到那些含有所有所需成分的食谱。

答案 4 :(得分:0)

您可以使用NOT EXISTS

试试这个:

SELECT DISTINCT Recipes.`name`
FROM Recipes JOIN Contains AS C1 USING (id_recipe) JOIN Ingredients USING(id_ingredient)
WHERE Ingredients.type = "bovin"
  AND NOT EXISTS (
    SELECT 1
    FROM Contains AS C2 JOIN Ingredients USING(id_ingredient)
    WHERE C1.id_recipe = C2.id_recipe
      AND Ingredients.type = "lactic"
)