MySQL исключая результаты на JOIN - PullRequest
1 голос
/ 19 ноября 2011

Я работаю над приложением CMS для ресторана.У меня есть отношение многие ко многим между 2 таблицами, menu_sections и menu_items.Связь поддерживается с таблицей между именованными menu_relationships.

В качестве примера, скажем, раздел меню под названием Закуски (menu_section_id = 1) содержит пункт меню под названием Крендельки (menu_item_id = 1) и раздел меню под названием Десерты (menu_section_id = 2) содержит пункт меню под названием Мороженое (menu_item_id = 2), но Мороженое также содержится в другом разделе меню под названием Детская еда (menu_section_id = 3).Таким образом, в таблице menu_relationships будет 3 строки, чтобы отобразить эти 3 отношения.Таблица отношений будет выглядеть следующим образом:

---------------------------------------
|   menu_section_id  |  menu_item_id  |
|=====================================|
|          1         |        1       |
|-------------------------------------|
|          2         |        2       |
|-------------------------------------|
|          3         |        2       |
---------------------------------------

Пока все хорошо.

Я хочу сгенерировать набор результатов, который будет возвращать имена всех элементов меню, кроме пунктов меню сучитывая menu_section_id.Итак, чтобы вернуть названия пунктов меню, у меня есть объединение в таблице menu_items.Вот SQL:

SELECT menu_section_id, menu_items.menu_item_id, menu_item_name 
FROM menu_relationships
JOIN menu_items 
ON menu_items.menu_item_id = menu_relationships.menu_item_id
WHERE menu_section_id != 2 

Результирующий набор, который даст мне строку для каждого отношения, которое не содержит данного menu_section_id.С данными примера я получу 2 строки обратно из таблицы отношений:

-----------------------------------------------------------
|   menu_section_id   |  menu_item_id  |  menu_item_name  |
|======================================|==================|
|          1          |        1       |     Pretzels     |
|--------------------------------------|------------------|
|          3          |        2       |     Ice Cream    |
-----------------------------------------------------------

Но я хочу полностью исключить пункт меню из набора результатов, если он имеет ЛЮБОЕ отношение к указанному menu_section_id.Другими словами, в этом примере я хочу вернуть только строки для пунктов меню, которые вообще не имеют сопоставлений отношений, в menu_section_id из 2, я хочу только вернуть строку Крендели .

Я пробовал разные вещи с GROUP BY и HAVING, используя агрегатную функцию bit_xor(), но пока мне не повезло в получении того, что я хочу.

Я, вероятно, могпотребовалось меньше времени, чтобы объяснить это, но я хотел, чтобы это было ясно, как я могу это сделать.Я надеюсь, что это так.Кто-нибудь может помочь?

Ответы [ 4 ]

2 голосов
/ 19 ноября 2011

Это прекрасный случай использования LEFT OUTER JOIN, поскольку он включает в себя все строки из левой таблицы и соответствует, где это возможно, возвращая NULL для любого несоответствия.

Построение наПример запроса Марка Брейера сверху, см. Этот пример:

SELECT R.menu_section_id, I.menu_item_id, I.menu_item_name
FROM menu_items AS I
LEFT OUTER JOIN menu_relationships R on (R.menu_item_id=I.menu_item_id) AND (R.menu_section_id = 2)

Оптимизатор mysql может переписать его как подзапрос - я не эксперт по оптимизации в любом случае - я бы посмотрел накак строятся ваши индексы, и посмотрите, имеет ли этот тип соединения смысл для вашей схемы.Я также проверил бы, действительно ли он быстрее, потому что на самом деле он менее семантический.

1 голос
/ 19 ноября 2011

Я бы использовал для этого подзапрос, получая каждый menu_item_id, который имеет menu_section_id 2, а затем используя NOT IN.Вот, пожалуйста:

SELECT menu_section_id, menu_items.menu_item_id, menu_item_name 
FROM menu_relationships
JOIN menu_items 
ON menu_items.menu_item_id = menu_relationships.menu_item_id
WHERE menu_relationships.menu_item_id NOT IN (
                                              SELECT menu_item_id 
                                              FROM menu_relationships 
                                              WHERE menu_section_id = 2
                                             );
1 голос
/ 19 ноября 2011

Есть много способов сделать это.Вот один пример использования WHERE ... NOT IN (...):

SELECT
    R.menu_section_id,
    I.menu_item_id,
    I.menu_item_name
FROM menu_items AS I
JOIN menu_relationships AS R
ON R.menu_item_id = I.menu_item_id
WHERE I.menu_item_id NOT IN
(
     SELECT menu_item_id  
     FROM menu_relationships
     WHERE menu_section_id = 2 
)
0 голосов
/ 19 ноября 2011

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

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

Как я уже сказал, вы, вероятно, будете в порядке.Просто не делайте подзапрос в подзапросе, если вы не хотите перезапустить свой сервер.

...