Mysql много ко многим отношениям.Фильтровать точное соответствие - PullRequest
1 голос
/ 11 июля 2019

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 |
+-------+---------+---------------+--------+------+

Как я могу получить еду только , в которой есть ингредиенты только с псевдонимами ингредиентов Картофель и курица?В моем примере результатом должен быть обед с курицей 2 Картошка 1?

Ответы [ 4 ]

2 голосов
/ 11 июля 2019

Вам нужно объединить все 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

См. Демоверсию .
Результаты:

| id  | name                             |
| --- | -------------------------------- |
| 3   | Meal with Chicken 2 and Potato 1 |
0 голосов
/ 12 июля 2019

У меня есть простой подход: - присоединиться к 4 столам и выбрать идентификатор еды, в которой не указано название ингредиента («картофель», «курица») - выбрать блюда, которые не входят в набор идентификаторов, найденный выше.Вот код (SQL-сервер):

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')
)
0 голосов
/ 11 июля 2019

Вы имеете дело с категориями ингредиентов, которые вы называете псевдонимами ингредиентов.Например, у вас есть категория «рис», которая может быть «белый рис», «красный рис» и любой другой рис.Вы ищете блюда, которые состоят именно из группы определенных категорий ингредиентов, например, «рис» и «курица».В худшем случае ваша еда содержит категорию несколько раз.Допустим, еда состоит из «курицы», «белого риса» и «красного риса».Несмотря на наличие трех ингредиентов, эта еда состоит только из двух категорий ингредиентов.

Начиная с 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 и напишите слеваприсоединиться и подзапрос без него (или сосчитать себя и заменить подзапрос на это число).

0 голосов
/ 11 июля 2019

Вот один метод:

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...