mysql多对多关系。过滤完全匹配

时间:2019-07-11 15:35:19

标签: mysql sql many-to-many

My schema

你好我要选择唯一具有确切成分别名的菜肴吗? 例如: 成分别名表:

+----+----------+
| id |   Name   |
+----+----------+
| 22 |  potato  |
| 23 |  rice    |
| 29 |  chicken |
+----+----------+

成分表:

+------+-----------+----------+-------+------+----------+
|  id  |   name    | proteins | carbs | fats | alias_id |
+------+-----------+----------+-------+------+----------+
| 3043 | Chicken 1 | 44.0     | 3.0   | 3.0  |       29 |
| 3025 | Rice 1    | 44.0     | 32.0  | 23   |       23 |
| 3024 | Rice 2    | 23.0     | 22.0  | 33.0 |       23 |
| 3042 | Chicken 2 | 22.0     | 22.0  | 3.0  |       29 |
| 3022 | Potato 1  | 22.0     | 22.0  | 32.0 |       22 |
| 3021 | Potato 2  | 20.0     | 30.0  | 40.0 |       22 |
| 3041 | Chicken 3 | 11.0     | 11.0  | 11.0 |       29 |
| 3026 | Rice 3    | 1.0      | 1.0   | 1.0  |       23 |
| 3023 | Potato 3  | 1.0      | 2.0   | 3.0  |       22 |
+------+-----------+----------+-------+------+----------+

餐桌餐:

+----+-----------------------------------------+
| id |                  name                   |
+----+-----------------------------------------+
|  1 | Meal with Chicken 1 and Rice 1          |
|  2 | Meal with Chicken 1 and Rice 2          |
|  3 | Meal with Chicken 2 Potato 1            |
|  4 | Meal with Chicken 2 Potato 1 and Rice 1 |
+----+-----------------------------------------+

餐食成分:

+-------+---------+---------------+--------+------+
|  id   | meal_id | ingredient_id | weight | role |
+-------+---------+---------------+--------+------+
| 13366 |       1 |          3043 |     13 |    1 |
| 13367 |       1 |          3025 |      1 |    1 |
| 13368 |       2 |          3043 |     12 |    2 |
| 13369 |       2 |          3024 |      8 |    3 |
| 13370 |       3 |          3042 |     22 |    1 |
| 13371 |       3 |          3022 |      1 |    1 |
| 13372 |       4 |          3042 |      3 |    1 |
| 13373 |       4 |          3022 |      3 |    3 |
| 13374 |       4 |          3024 |      2 |    2 |
+-------+---------+---------------+--------+------+

我如何进餐,其中的食材只有且别名为土豆和鸡肉?在我的示例中,结果必须是ID为3的餐点,餐点为鸡肉2马铃薯1?

4 个答案:

答案 0 :(得分:2)

您需要按膳食分组连接所有4个表,并将条件放入HAVING子句:

select m.id, m.name
from ingredient_alias ia
inner join ingredient i on i.alias_id = ia.id
inner join meal_ingredient mi on mi.ingredient_id = i.id
inner join meal m on m.id = mi.meal_id
group by m.id, m.name
having 
  count(distinct ia.name) = 2
  and 
  sum(ia.name not in ('potato', 'chicken')) = 0

请参见demo
结果:

| id  | name                             |
| --- | -------------------------------- |
| 3   | Meal with Chicken 2 and Potato 1 |

答案 1 :(得分:0)

这是一种方法:

select m.meal_id
from meal_ingredient m join
     ingredient i
     on m.ingredient_id = i.id left join
     ingredient_alias ia
     on ia.id = i.ingredient_alias_id
group by m.meal_id
having count(ia.id) = count(*) and                           -- all ingredients in meal match list
       count(*) = (select count(*) from ingredient_alias)    -- all ingredients in list are present

如果可以重复,则第二个条件应该是count(distinct m.ingredient_id),而不是count(*)

答案 2 :(得分:0)

您正在处理被称为成分别名的成分类别。例如。您有“米”类别,可以是“白米”,“红米”以及其他任何大米。您正在寻找的食物正好包含一组给定的成分类别,例如'米饭和鸡肉'。在最坏的情况下,您的餐食包含多次类别。可以说一顿饭包括“鸡肉”,“白米饭”和“红米饭”。尽管具有三种成分,但该餐只包含两种成分。

从MySQL 8开始:

with ia as
(
  select id from ingredient_alias where name in ('potato', 'chicken')
)
select *
from meal
where id in
(
  select mi.meal_id
  from meal_ingredient mi
  join ingredient i on i.id = mi.ingredient_id
  left join ia on ia.id = i.alias_id
  group by mi.meal_id
  having count(distinct ia.id) = count(distinct i.alias_id)
     and count(distinct ia.id) = (select count(*) from ia)
);

对于较旧的版本,请删除WITH子句,然后编写左联接和不带左联接的子查询(或者算一下自己,并用该数字替换子查询)。

答案 3 :(得分:0)

我有一个简单的方法: -加入4个表格并选择成分名称中未包含的餐点ID(“土豆”,“鸡肉”) -在上方未找到的ID中选择ID不作为的餐点。 这里的代码(SQL Server):

select * from test.dbo.meal where test.dbo.meal.id not in (
  select m.id from test.dbo.meal m 
      inner  join test.dbo.meal_ingredient mi on m.id = mi.meal_id
      inner  join test.dbo.Ingredient i on i.id = mi.ingredient_id
      inner  join test.dbo.Ingredient_alias ia on ia.id = i.Ingredient_alias_id
  where ia.name not in ('potato', 'chicken')
)