仅当全部存在时,SQL查询INNER JOIN

时间:2017-08-01 11:32:34

标签: mysql sql database select

我有两个表,一个包含不同的食谱,另一个包含recipes_ingredients(食谱所需的所有成分)。

Sql Azure Db

让我们说我只选择了一种成分(例如鸡蛋)。

Azure VM Sql

这会返回包含鸡蛋的所有食谱。这是合乎逻辑的,但我想创建一个查询,返回 ONLY 包含鸡蛋(例如煮鸡蛋)的所有食谱。不是一些将鸡蛋作为其中一部分的食谱。

如果我同时选择鸡蛋和牛奶并创建相同的查询:

recipes
-----
id_recipe
name
description
etc...

recipe_ingredients
-----
id_recipe_ingredient
id_recipe
id_ingredient

我会找到煎蛋卷,但我也会找到所有其他含有鸡蛋和牛奶的食谱。

我想选择的是:

  • 仅包含鸡蛋的食谱

  • 仅包含鸡蛋和牛奶的食谱

我不想选择含有鸡蛋,牛奶,面粉和糖的蛋糕。

感谢任何可能让我朝着正确方向前进的答案!

4 个答案:

答案 0 :(得分:4)

计算配料的数量和配料的数量,并检查它们是否匹配。例如,要检索包含成分1和2以及仅包含这两种成分的所有配方:

SELECT id_recipe
FROM recipe_ingredients
GROUP BY id_recipe
HAVING COUNT(*) = 2
  AND COUNT(CASE WHEN id_ingredient IN (1, 2) THEN 1 END) = COUNT(*);

您还可以使用GROUP_CONCAT将成分ID收集到字符串中并进行字符串比较:

SELECT id_recipe
FROM recipe_ingredients
GROUP BY id_recipe
HAVING GROUP_CONCAT(id_ingredient ORDER BY id_ingredient) = '1,2';

在前面的查询中,为了便于阅读,我省略了完整的配方检索。为完整起见,以下查询将检索完整的配方:

SELECT *
FROM recipes JOIN recipe_ingredients USING (id_recipe)
WHERE id_recipe in (
    SELECT id_recipe
    FROM recipe_ingredients
    GROUP BY id_recipe
    HAVING COUNT(*) = 2
      AND COUNT(CASE WHEN id_ingredient IN (1, 2) THEN 1 END) = COUNT(*)
);

虽然这些查询简短且易读,但如果您有大量食谱和/或成分,则可能会遇到性能问题。如果这样做,您可能希望在id_ingredientid_recipe上添加索引并尝试将查询重写为

SELECT *
FROM recipes JOIN recipe_ingredients USING (id_recipe)
WHERE id_recipe in (
    SELECT i1.id_recipe
    FROM recipe_ingredients i1
      JOIN recipe_ingredients i2
        ON (i1.id_recipe = i2.id_recipe AND i2.id_ingredient = 2)
      LEFT JOIN recipe_ingredients ni
        ON (i1.id_recipe = ni.id_recipe and ni.id_ingredient NOT IN (1, 2))
    WHERE i1.id_ingredient = 1 AND ni.id_ingredient IS NULL
);

但是,我发现这个版本更加繁琐且难以维护,并建议在性能需要之前避免使用它。

答案 1 :(得分:1)

获得EGG和MILK只含有2种成分的产品和含有EGG且只含一种成分的UNION ........

SELECT r.Id, COUNT(ri.Id) FROM recipes r                            --GET THE RECIPE ID AND COUNT OF INGREDIENTS
INNER JOIN recipes_ingredients ri ON r.id_recipe = ri.id_recipe     --JOIN RECIPE INGREDIENTS
WHERE 
ri.id_ingredient IN (1,2)                                           --EGGS OR MILK
AND r.id NOT IN (SELECT ri2.id FROM recipes_ingredients ri2 WHERE ri2.id NOT IN (1,2)) --AND JUST EGGSD OR MILK
HAVING COUNT(ri.Id) = 2                                             --AND THERE ARE 2 INGREDIENTS
UNION ALL
SELECT r.Id, COUNT(ri.Id) FROM recipes r                            --GET THE RECIPE ID AND COUNT OF INGREDIENTS
INNER JOIN recipes_ingredients ri ON r.id_recipe = ri.id_recipe     --JOIN RECIPE INGREDIENTS
WHERE 
ri.id_ingredient = 1                                                --EGGS
AND r.id NOT IN (SELECT ri2.id FROM recipes_ingredients ri2 WHERE ri2.id !=1)  --AND JUST EGGS
HAVING COUNT(ri.Id) = 1     

答案 2 :(得分:1)

查询单个成分,并确保通过将查询中的成分数量与配方成分的数量相匹配,不再有任何成分。

修改

逐个配对所需的成分,以确保所有这些成分都存在于所讨论的配方中。如果它们都存在并且配料的数量匹配,那么就不会有任何更多或更少的成分。

select * 
from recipe_table rt
where rt.id_recipe in (select rig.id_recipe
                    from recipe_ingredient_table rig
                    where rig.id_recipe = rt.id_recipe
                      and <ing_id_1> in (select rig2.id_ingredient from recipe_ingredient_table rig2 where rig2.id_recipe = rig.id_recipe)
                      and <ing_id_2> in (select rig2.id_ingredient from recipe_ingredient_table rig2 where rig2.id_recipe = rig.id_recipe)
                      and ...
                      and (select count(*) 
                           from recipe_ingredient_table
                           where id_recipe = rig.id_recipe) = <number of ingredients>
          )

答案 3 :(得分:-1)

-- 1 egg only
SELECT *
FROM recipes AS r
WHERE EXISTS (
    SELECT ri.id_recipe, count(*)
    FROM recipes_ingredients AS ri
    WHERE ri.id_recipe = id_recipe 
    AND ri.id_ingredient = 1
    GROUP BY ri.id_recipe
    HAVING COUNT(*) = 1
)

-- 1 egg only OR 2 milk only OR egg and milk
SELECT *
FROM recipes AS r
WHERE EXISTS (
    SELECT ri.id_recipe, count(*)
    FROM recipes_ingredients AS ri
    WHERE ri.id_recipe = id_recipe 
    AND ri.id_ingredient IN (1, 2)
    GROUP BY ri.id_recipe
    HAVING COUNT(*) < 3
)

-- both egg and milk only
-- change above SQL: HAVING COUNT(*) = 2