2013-05-14 2 views
1

Готов поспорить, это простой вопрос, но мои отломы MySQL немного ржавые. Необходимо немного помочь создать правильный запрос для следующего сценария с несколькими условными соединениями. У меня есть одна таблица для атрибутов (думаю теги) и другая таблица, которая имеет список записей. Запуск проблем, когда я хочу сопоставить страницу с более чем одним атрибутом, определенным в запросе.Multi-conditional join

+--------------------------------------+ 
| ATTRIBUTES       | 
+-----------+------------+-------------+ 
| name  | value  | page_id  | 
+-----------+------------+-------------+ 
| type  food   2   | 
| fruit  apple  2   | 
| color  green  2   | 
| type  frog   3   | 
| color  green  3   | 
+--------------------------------------+ 


+--------------------------------------+ 
| PAGES        | 
+--------------+-----------------------+ 
| page_id  | title     | 
+--------------+-----------------------+ 
| 2    Granny Smith   | 
| 3    Kermit the Frog  | 
+--------------------------------------+ 

ПРИМЕРЫ ИСПОЛЬЗОВАНИЯ

Query: type+food 
Results: Granny Smith 

Query: color+green 
Results: Granny Smith, Kermit the Frog 

Query: type+food and color+green 
Results: Granny Smith 

Это запрос, я играл с, но, очевидно, не работает:

SELECT page.page_id, page.title 
FROM page_attributes 
LEFT JOIN page ON (page_attributes.page_id = page.page_id) 
WHERE (page_attributes.name = 'color' 
AND page_attributes.value = 'green') 
AND (page_attributes.name = 'type' 
AND page_attributes.value = 'food') 
+0

Покажите нам, какой SQL вы пробовали, пожалуйста. –

+1

Какой у вас запрос, который не работает? –

+1

, если вы только хотите, чтобы результаты были указаны, то вам совсем не нужно присоединяться ... – luksch

ответ

0

Наряду с теми же линиями, что и у Nitro, но сохраняйте критерии атрибутов с предложением ON. Кроме того, убедитесь, что у вас есть индекс в таблице атрибутов (page_id, name, value), чтобы объединения были оптимизированы.

SELECT 
     page.page_id, 
     page.title 
    FROM 
     page 

     INNER JOIN page_attributes a1 
      ON page.page_id = a1.page_id 
      AND a1.name = 'color' 
      AND a1.value = 'green' 

     INNER JOIN page_attributes a2 
      ON page.page_id = a2.page_id 
      AND a2.name = 'type' 
      AND a2.value = 'food' 

    LIMIT 10 

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

+0

Работает идеально и легко для автоматизации. Благодарю. –

+0

@ LouisW, я подозревал, что вы будете изучать автоматизацию, и да, это проще, чем несколько объединений и настройка предложения WHERE. Однако оба решения - хорошие варианты. – DRapp

2

Один из способов сделать это, даже если там может быть более эффективный способ - обрабатывать запрос с несколькими INNER JOIN s, по одному для каждого атрибута. С INNER JOIN, в отличие от LEFT JOIN, все условия должны быть истинными для соответствия главной строки.

SELECT page.page_id, page.title 
    FROM page 
INNER JOIN page_attributes a1 ON (a1.page_id = page.page_id) 
INNER JOIN page_attributes a2 ON (a2.page_id = page.page_id) 
WHERE (a1.name = 'color' AND a1.value = 'green') 
    AND (a2.name = 'type' AND a2.value = 'food') 
LIMIT 10 
0

Проблема в том, что вы пытаетесь применить все условия одновременно к различным строкам соединения. Вот что присоединились соотношение (перед любым SELECT или WHERE фильтрации) может выглядеть следующим образом:

Joined relation: 
+--------------+-----------------------+-------------+-------------+ 
| page_id  | title     | name  | value  | 
+--------------+-----------------------+-------------+-------------+ 
| 2    Granny Smith   type   food  | 
| 2    Granny Smith   color   green  | 
| 2    Granny Smith   fruit   apple  | 
| 3    Kermit the Frog   color   green  | 
| 3    Kermit the Frog   type   frog  | 
+--------------+-----------------------+-------------+-------------+ 

ИНЕКЕ будет применяться к каждой строке этого промежуточного результата. Итак, предложение в вашем запросе:

WHERE (page_attributes.name = 'color' 
AND page_attributes.value = 'green') 
AND (page_attributes.name = 'type' 
AND page_attributes.value = 'food') 

будет ложным для каждой строки этого объединенного отношения.

Простое изменение AND s на OR s не будет работать, потому что это просто найдет страницы с ЛЮБОЙ из значений тегов, а не ВСЕ из них, что и действительно нужно сделать.

К сожалению, в этой структуре таблицы нет общего стандартизованного способа сделать это для произвольного количества запросов, которые запрашиваются. Предложения в других ответах на использование нескольких объединений в таблице page_attributes будут работать, если вы заранее знаете, сколько тегов будет запрашиваться, и вы можете добиться аналогичного эффекта с несколькими UNION редакционными запросами. Но опять же, они будут работать, только если вы исправите количество тегов для запроса.

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