Объединение 3 таблиц с отношением n: m, хотите видеть несовпадающие строки

#mysql #join

#mysql #Присоединиться

Вопрос:

Для решения этой проблемы рассмотрим следующие 3 таблицы:

 Event
id (pk)
title

Event_Category
event_id (pk, fk)
category_id (pk, fk)

Category
id (pk)
description
  

Довольно тривиально, я думаю … 🙂 Каждое событие может попасть в ноль или более категорий, всего существует 4 категории.
В моем приложении я хочу просматривать и редактировать категории для определенного события. Графически событие будет показано вместе со ВСЕМИ категориями и флажком, указывающим, попадает ли событие в категорию. Изменение и сохранение выбора приведет к изменению промежуточной таблицы Event_Category.
Но сначала: как выбрать это для конкретного события? Нужный мне запрос фактически всегда будет возвращать 4 строки, количество присутствующих категорий.

Далее возвращаются только записи для категорий, в которые попадает событие с идентификатором=11. Эксперименты с внешними объединениями не дали больше строк в результате.

 SELECT e.id, c.omschrijving 
FROM Event e
  INNER JOIN Event_Categorie ec ON e.id = ec.event_id
  INNER JOIN Categorie c ON c.id = ec.categorie_id
WHERE e.id = 11
  

Или мне следует начать с таблицы категорий в запросе? Надеюсь на некоторые подсказки 🙂
TIA, Klaas

ОБНОВЛЕНИЕ: Да, я сделал, но до сих пор не нашел ответа. Но я упростил проблему, исключив таблицу событий из запроса, потому что эта таблица используется только для просмотра описаний событий.

 SELECT * from Categorie c LEFT JOIN Event_Categorie ec ON c.id = ec.categorie_id WHERE ec.event_id = 11;
  

Упрощенный запрос из 2 таблиц использует только таблицу поиска и таблицу ссылок, но по-прежнему возвращает только 2 строки вместо общего количества 4 строк в таблице категорий.
Я предполагаю, что предложение WHERE применяется после объединения, поэтому строки, не присоединенные к таблице ссылок, исключаются. В моем приложении я решил проблемы с помощью подзапроса, но я все еще хотел бы знать, какое решение является лучшим.

Комментарии:

1. такого рода вопросы задавались и на них давались ответы здесь много раз раньше. Вы пытались выполнить поиск в StackOverflow перед публикацией этого?

2. Да, я сделал, но до сих пор не нашел ответа. Но я упростил проблему.

3. смотрите обновление под моим первоначальным вопросом

Ответ №1:

Вам нужен список всех категорий, а также информация о том, есть ли эта категория в списке категорий вашего события.

Итак, вы можете сделать:

 SELECT 
    * 
FROM 
    Category 
    LEFT JOIN Event_Category ON category_id = id 
WHERE 
    event_id = 11
  

и столбец event_id будет равен нулю для категорий, которые не являются частью вашего события.

Вы также можете создать столбец (с именем has_category ниже), который вы будете использовать, чтобы увидеть, имеет ли событие эту категорию, вместо сравнения с NULL:

 SELECT 
    *,
    event_id IS NOT NULL AS has_category
FROM 
    Category 
    LEFT JOIN Event_Category ON category_id = id 
WHERE 
    event_id = 11
  

РЕДАКТИРОВАТЬ: Похоже, это именно то, что вы говорите, что делаете при редактировании. Я протестировал это, и это кажется правильным. Вы уверены, что выполняете этот запрос, и что строки с NULL каким-либо образом не игнорируются?

Комментарии:

1. Смотрите выше, оправдания за плохой макет, все еще выясняю, как это работает 🙂

2. итак … оба ваших запроса возвращают только 2 строки, только та, у которой есть подвыборка, работает по мере необходимости.

3. какую платформу вы используете? это не то, что я вижу в обычной настройке MySQL, выполняя запросы на терминале.

4. Я использую Ubuntu Linux с Apache, действительно, я запускаю запросы из консоли для тестирования, я также использую phpMyAdmin, поскольку есть несколько хороших графических клиентов MySQL для Linux.

Ответ №2:

Запрос

 SELECT * FROM Categorie;
  

возвращает 4 строки:

  ---- -------------- ------------------------------------- -------------------------------------- 
| id | omschrijving | afbeelding                          | afbeelding_klein                     |
 ---- -------------- ------------------------------------- -------------------------------------- 
|  1 | Creatief     | images/categorieen/creatief420k.jpg | images/categorieen/creatief190k.jpg  |
|  2 | Sportief     | images/categorieen/sportief420k.jpg | images/categorieen/sportief190kr.jpg |
|  4 | Culinair     | images/categorieen/culinair420k.jpg | images/categorieen/culinair190k.jpg  |
|  5 | Spirit       | images/categorieen/spirit420k.jpg   | images/categorieen/spirit190k.jpg    |
 ---- -------------- ------------------------------------- -------------------------------------- 
4 rows in set (0.00 sec)
  

НО:
Запрос

 SELECT * 
FROM Categorie 
LEFT JOIN Event_Categorie ON categorie_id = id 
WHERE event_id = 11;
  

возвращает 2 строки:

  ---- -------------- ------------------------------------- ------------------------------------- ---------- -------------- 
| id | omschrijving | afbeelding                          | afbeelding_klein                    | event_id | categorie_id |
 ---- -------------- ------------------------------------- ------------------------------------- ---------- -------------- 
|  1 | Creatief     | images/categorieen/creatief420k.jpg | images/categorieen/creatief190k.jpg |       11 |            1 |
|  4 | Culinair     | images/categorieen/culinair420k.jpg | images/categorieen/culinair190k.jpg |       11 |            4 |
 ---- -------------- ------------------------------------- ------------------------------------- ---------- -------------- 
2 rows in set (0.00 sec)
  

Поэтому мне все еще нужен подзапрос… и ЛЕВОЕ СОЕДИНЕНИЕ неэффективно для отображения всех строк таблицы категорий, независимо от того, есть ли совпадение с таблицей ссылок.

Однако этот запрос выполняет то, что я хочу, чтобы он делал:

 SELECT * 
FROM Categorie c 
LEFT JOIN (SELECT * FROM Event_Categorie ec WHERE ec.event_id = 11 ) AS subselect ON  subselect.categorie_id = c.id;
  

Результат:

  ---- -------------- ------------------------------------- -------------------------------------- ---------- -------------- 
| id | omschrijving | afbeelding                          | afbeelding_klein                     | event_id | categorie_id |
 ---- -------------- ------------------------------------- -------------------------------------- ---------- -------------- 
|  1 | Creatief     | images/categorieen/creatief420k.jpg | images/categorieen/creatief190k.jpg  |       11 |            1 |
|  2 | Sportief     | images/categorieen/sportief420k.jpg | images/categorieen/sportief190kr.jpg |     NULL |         NULL |
|  4 | Culinair     | images/categorieen/culinair420k.jpg | images/categorieen/culinair190k.jpg  |       11 |            4 |
|  5 | Spirit       | images/categorieen/spirit420k.jpg   | images/categorieen/spirit190k.jpg    |     NULL |         NULL |
 ---- -------------- ------------------------------------- -------------------------------------- ---------- -------------- 
4 rows in set (0.00 sec)
  

Комментарии:

1. И я все еще не понимаю, почему первый запрос возвращает только 2 строки, несмотря на использование ЛЕВОГО СОЕДИНЕНИЯ … 🙁

Ответ №3:

Проблема в том, что вы отфильтровали результаты по идентификатору события. Как вы можете видеть в своих результатах, в двух категориях (Sportief и Spirit) нет событий. Таким образом, правильный оператор SQL (с использованием синтаксиса SQL Server; может потребоваться некоторый перевод) является:

 SELECT * 
FROM Categorie 
LEFT JOIN Event_Categorie ON categorie_id = id 
WHERE (event_id IS NULL) OR (event_id = 11);
  

Комментарии:

1. Я протестировал это, и оно по-прежнему возвращает только 2 строки… не так уж странно, потому что отсутствующие строки — это те, которые не совпадают с таблицей Event_Category, поэтому проверка event)_id на ноль не помогает. По-прежнему единственным рабочим запросом является запрос с подвыборкой.

2. Более того, поле event_id таблицы ссылок никогда не имеет значения NULL, потому что оно является частью первичного ключа! Действительно, при обязательном исключении предложения WHERE результат содержит 44 строки, но ни одна из них с event_id = null. Так что это явно не причина.

3. Когда вы выполняете левое объединение, если в таблице Event_Categorie нет элементов для данной категории, то event_id должен быть нулевым. Это точка ЛЕВОГО ОБЪЕДИНЕНИЯ; укажите все записи в первой таблице и, если во второй таблице есть какие-либо записи, соответствующие условию объединения, также покажите их, в противном случае покажите нули для второй таблицы. Вы можете увидеть это точное поведение в вашей последней таблице результатов, где каждая вторая строка имеет нулевой event_id.

Ответ №4:

Наконец-то я нашел правильный запрос, никакого дополнительного выбора не требуется. Но предложение WHERE работает после объединения и, следовательно, больше не является частью объединения. Решение заключается в расширении предложения ON дополнительным условием. Теперь все 4 строки возвращаются с NULL для несоответствующих категорий!

 SELECT * 
FROM Categorie  
LEFT JOIN Event_Categorie ON categorie_id = id AND event_id = 11;
  

Итак, суть в том, что добавление дополнительного условия в предложение ON имеет иной эффект, чем фильтрация строк по тому же условию в предложении WHERE!