MySQL запрашивает реляционные таблицы - PullRequest
0 голосов
/ 28 июля 2011

Я полагаю, это более или менее тривиальная вещь, но здесь это идет ...

У меня есть несколько реляционных таблиц, и я пытаюсь получить строки в зависимости от 2 значений. Вот мой запрос до сих пор:

SELECT `inventory`.*, `tags`.*, `tags_inv`.*, `categories`.*, `categories_inv`.*
FROM (`tags`)
LEFT JOIN `tags_inv` ON `tags_inv`.`tag_id` = `tags`.`tag_id`
LEFT JOIN `inventory` ON `inventory`.`inventory_id` = `tags_inv`.`inv_id`
LEFT JOIN `categories_inv` ON `categories_inv`.`inv_id` = `inventory`.`inventory_id`
LEFT JOIN `categories` ON `categories`.`cat_id` = `categories_inv`.`cat_id`
WHERE `categories`.`cat_id` = 1
AND  `inventory`.`inv_name`  LIKE '%p%'
OR  `inventory`.`inv_dim_w`  LIKE '%p%'
OR  `inventory`.`inv_dim_l`  LIKE '%p%'
OR  `inventory`.`inv_dim_h`  LIKE '%p%'
OR  `tags`.`tag_description`  LIKE '%p%'
ORDER BY `inventory`.`inv_name`

Проблема в том, что мой запрос выбирает все из моей базы данных (как будто он игнорирует cat_id), но я хотел бы выбрать только строки с cat_id 1 в этом случае ...
Есть ли быстрый и разумный способ решить эту проблему? :)

Заранее спасибо!

Ответы [ 2 ]

4 голосов
/ 28 июля 2011
  • Напомните себе, что означает a LEFT JOIN b. Это означает, что мы хотим все строк из таблицы a, есть ли соответствующие строки в таблице b или нет. Вы используете LEFT JOIN для всех своих объединений, но ваши условия в предложении WHERE подразумевают, что вы действительно хотите INNER JOIN.

  • Пересмотреть приоритет булевых операторов. AND связывается более тесно, чем OR (это верно для всех языков программирования, не только для SQL), поэтому ваше предложение WHERE эквивалентно:

    WHERE (`categories`.`cat_id` = 1 AND `inventory`.`inv_name`  LIKE '%p%')
      OR  `inventory`.`inv_dim_w`  LIKE '%p%'
      OR  `inventory`.`inv_dim_l`  LIKE '%p%'
      OR  `inventory`.`inv_dim_h`  LIKE '%p%'
      OR  `tags`.`tag_description`  LIKE '%p%'
    

    Вам необходимо использовать скобки для управления естественным приоритетом оператора.

    WHERE `categories`.`cat_id` = 1 AND 
      (`inventory`.`inv_name`  LIKE '%p%'
      OR  `inventory`.`inv_dim_w`  LIKE '%p%'
      OR  `inventory`.`inv_dim_l`  LIKE '%p%'
      OR  `inventory`.`inv_dim_h`  LIKE '%p%'
      OR  `tags`.`tag_description`  LIKE '%p%')
    
  • Использование предиката сопоставления с шаблоном LIKE с ведущими подстановочными знаками означает, что ваш запрос не может использовать индекс для повышения эффективности. Таким образом, он должен прочитать каждую строку ваших таблиц инвентаря и тегов, чтобы оценить соответствие шаблона. Чтобы это исправить, вы можете использовать инструмент полнотекстового поиска или добавить еще один столбец в таблицы, содержащий только «p», который вы ищете.

    WHERE `categories`.`cat_id` = 1 
      AND (`inventory`.`has_p` = 1 OR `tags`.`has_p` = 1)
    
  • Трудно оптимизировать выражения OR с помощью индексов, поскольку в большинстве случаев MySQL имеет ограничение, согласно которому он может использовать только один индекс для каждой ссылки на таблицу. Обходной путь - выполнить несколько запросов и объединить их вместе. Таким образом, каждая ссылка на таблицу использует свой собственный индекс.

    SELECT ... WHERE `categories`.`cat_id` = 1 AND `inventory`.`has_p` = 1
    UNION ALL
    SELECT ... WHERE `categories`.`cat_id` = 1 AND `tags`.`has_p` = 1
    
4 голосов
/ 28 июля 2011

Используйте скобки в предложении WHERE, чтобы сгруппировать все условия OR вместе.Поскольку у вас И и ИЛИ на одном уровне в ГДЕ, он игнорирует фильтр cat_id = 1 для всех ветвей ИЛИ.

...