【问题标题】:How can I find all the rows in a table that exclusively have related rows in a given list?如何在给定列表中找到唯一具有相关行的表中的所有行?
【发布时间】:2025-12-16 23:20:08
【问题描述】:

我有三张桌子。将它们视为以下内容:

  Recipes
  id | name
  1  | Cookies
  2  | Soup
  ...


  Ingredients
  id | name
  1  | flour
  2  | butter
  3  | chicken
  ...

  Recipe_Ingredient
  recipe_id | ingredient_id
  1         | 1
  1         | 2
  2         | 3

希望你能理解。我想要的是一个查询,我可以在其中找到所有配方,其中的成分是给定成分集的子集。

我的想法是,我想列出我手头上可以做的所有事情(但当然不是我手头上的所有东西。)

我尝试使用各种级别的子查询和与 EXISTS 相关的子查询来实现这一点,但没有运气。我也尝试使用 HAVING 和 COUNT,但这似乎只适用于我想要使用我手头所有成分的东西。

【问题讨论】:

  • 因此您需要所有成分都在“可用”成分列表中的食谱。尝试 [sql-match-all] 标记或右侧的链接,在 Related 标题下。
  • @ypercube 哇,太棒了。是的,这实际上是我尝试过的,但我一定是我的逻辑搞砸了。感谢您的精彩链接
  • 这个问题有10多种方法可以实现这个How to filter SQL results in a has-many-through relation。也进行了基准测试。更通用的(如果您的可用成分在表格或列表中,并且您不想动态创建 SQL 查询),答案是 1、2、7、8。
  • 对,最终这些将在一个表中

标签: sql sql-match-all


【解决方案1】:

测试它是否有效。假设您的(唯一)成分 ID 在名为 ingredients_avail 的表中可用,则此查询应显示具有完整成分的配方 ID:

    SELECT recipe_id
    FROM [select recipe_id, count(*) as num_of_ingredients from recipe_ingredient group by recipe_id]. AS x, [select recipe_ingredient.recipe_id as recipe_id, count(*) as num_of_ingredients 
    from recipe_ingredient, ingredients_avail               
    where               
     recipe_ingredient.ingredient_id = ingredients_avail.ingredient_id
    group by recipe_ingredient.recipe_id]. AS y               
    WHERE x.recipe_id = y.recipe_id and               
    x.num_of_ingredients = y.num_of_ingredients;               

另外,这里是更典型的语法:

  SELECT x.recipe_id 
    FROM ( 
      SELECT recipe_id, count(*) as num_of_ingredients 
        FROM recipe_ingredients
      GROUP BY recipe_id
     ) x, ( 
      SELECT recipe_id, count(*) as num_of_ingredients 
        FROM recipe_ingredients
        JOIN ingredients_avail 
          ON recipe_ingredients.ingredient_id = ingredients_avail.ingredient_id 
      GROUP BY recipe_id
     ) y 
  WHERE x.recipe_id = y.recipe_id AND x.num_of_ingredients = y.num_of_ingredients;

【讨论】:

    【解决方案2】:
        mysql>
        mysql> select * from ingredients;
        +------+---------------+-----------+
        | id   | name          | available |
        +------+---------------+-----------+
        |    1 | salt          | n         |
        |    2 | sugar         | n         |
        |    3 | flour         | n         |
        |    4 | butter        | n         |
        |    5 | vanilla       | n         |
        |    6 | baking powder | n         |
        |    7 | egg           | n         |
        +------+---------------+-----------+
        7 rows in set (0.00 sec)
    
        mysql> select * from recipes;
        +------+---------------+
        | id   | name          |
        +------+---------------+
        |    1 | cookie        |
        |    2 | soup          |
        |    3 | xtreme flavor |
        +------+---------------+
        3 rows in set (0.00 sec)
    
        mysql> select * from recipe_ingredient;
        +-----------+---------------+
        | recipe_id | ingredient_id |
        +-----------+---------------+
        |         1 |             1 |
        |         1 |             2 |
        |         1 |             3 |
        |         1 |             4 |
        |         1 |             5 |
        |         1 |             6 |
        |         1 |             7 |
        |         2 |             1 |
        |         2 |             7 |
        |         3 |             4 |
        |         3 |             3 |
        +-----------+---------------+
        11 rows in set (0.00 sec)
    
        mysql>
        mysql> update ingredients set available = 'n';
        Query OK, 0 rows affected (0.00 sec)
        Rows matched: 7  Changed: 0  Warnings: 0
    
        mysql>
        mysql> update ingredients set available = 'y'
            -> where id in  (1,2,3,4,5,6,7);
        Query OK, 7 rows affected (0.00 sec)
        Rows matched: 7  Changed: 7  Warnings: 0
    
        mysql>
        mysql> select recipes.name from
            -> (select recipe_id, available from
            -> recipe_ingredient,
            -> ingredients
            -> where ingredient_id = ingredients.id
            -> group by  recipe_id, available) x, recipes
            -> where recipes.id = x.recipe_id
            -> group by x.recipe_id
            -> having count(*) = 1
            -> and max(x.available) = 'y';
        +---------------+
        | name          |
        +---------------+
        | cookie        |
        | soup          |
        | xtreme flavor |
        +---------------+
        3 rows in set (0.06 sec)
    
        mysql>
        mysql> update ingredients set available = 'n';
        Query OK, 7 rows affected (0.00 sec)
        Rows matched: 7  Changed: 7  Warnings: 0
    
        mysql>
        mysql> update ingredients set available = 'y'
            -> where id in  (1,7);
        Query OK, 2 rows affected (0.00 sec)
        Rows matched: 2  Changed: 2  Warnings: 0
    
        mysql>
        mysql> select recipes.name from
            -> (select recipe_id, available from
            -> recipe_ingredient,
            -> ingredients
            -> where ingredient_id = ingredients.id
            -> group by  recipe_id, available) x, recipes
            -> where recipes.id = x.recipe_id
            -> group by x.recipe_id
            -> having count(*) = 1
            -> and max(x.available) = 'y';
        +------+
        | name |
        +------+
        | soup |
        +------+
        1 row in set (0.06 sec)
    
        mysql>
        mysql>
        mysql> update ingredients set available = 'n';
        Query OK, 2 rows affected (0.00 sec)
        Rows matched: 7  Changed: 2  Warnings: 0
    
        mysql>
        mysql> update ingredients set available = 'y'
            -> where id in  (4,3);
        Query OK, 2 rows affected (0.00 sec)
        Rows matched: 2  Changed: 2  Warnings: 0
    
        mysql>
        mysql> select recipes.name from
            -> (select recipe_id, available from
            -> recipe_ingredient,
            -> ingredients
            -> where ingredient_id = ingredients.id
            -> group by  recipe_id, available) x, recipes
            -> where recipes.id = x.recipe_id
            -> group by x.recipe_id
            -> having count(*) = 1
            -> and max(x.available) = 'y';
        +---------------+
        | name          |
        +---------------+
        | xtreme flavor |
        +---------------+
        1 row in set (0.05 sec)
    
        mysql>
        mysql>
        mysql> update ingredients set available = 'n';
        Query OK, 3 rows affected (0.00 sec)
        Rows matched: 7  Changed: 3  Warnings: 0
    
        mysql>
        mysql> update ingredients set available = 'y'
            -> where id in  (1,3,7);
        Query OK, 3 rows affected (0.00 sec)
        Rows matched: 3  Changed: 3  Warnings: 0
    
        mysql>
        mysql> select recipes.name from
            -> (select recipe_id, available from
            -> recipe_ingredient,
            -> ingredients
            -> where ingredient_id = ingredients.id
            -> group by  recipe_id, available) x, recipes
            -> where recipes.id = x.recipe_id
            -> group by x.recipe_id
            -> having count(*) = 1
            -> and max(x.available) = 'y';
        +------+
        | name |
        +------+
        | soup |
        +------+
        1 row in set (0.06 sec)
    

    【讨论】:

    • 如果我有可用的成分 1、3 和 7,这是否正确工作?也就是说,它应该返回汤,因为我可以用 1 和 7 做汤。
    • @Frew,是的,当 1、3 和 7 可用时,它确实返回“汤”。我在上面添加了结果。
    • 太棒了,谢谢;我会看看这是否适合我,如果是这样,你会得到信用:-)
    • 实际上,我在弄清楚如何将其转换为我的架构时遇到了很多麻烦;也就是说,一个 id 列表或一个子查询,而不是可用的标志列。我能得到一些帮助吗?
    最近更新 更多