MySQL: запрос всех записей, имеющих данный набор отношений, к другой таблице - PullRequest
0 голосов
/ 19 августа 2010

Да, довольно тупой заголовок, но мне было трудно описать мою проблему. У меня есть следующие таблицы:

свойства

id | name
1  | color
2  | material

опции

id | property_id | name
1  | 1           | yellow
2  | 1           | blue
3  | 2           | wood
4  | 2           | stone

вещества

id | name
1  | orange juice
2  | cheese

1012 * Отношения * id | substance_id | option_id 1 | 2 | 1 2 | 2 | 3 3 | 1 | 1 Теперь у меня есть список вариантов, и я хочу знать, какие вещества относятся ко всем этим параметрам. (Например, какие вещества желтые и сделаны из дерева?) Возможно ли это одним запросом? Я пытаюсь сделать это в Rails.

Ответы [ 3 ]

0 голосов
/ 19 августа 2010
(SELECT s.name FROM substances s, relations r, options o
WHERE r.substance_id = s.id and r.option_id = o.id and o.name='yellow')
INTERSECT
(SELECT s.name FROM substances s, relations r, options o
WHERE r.substance_id = s.id and r.option_id = o.id and o.name='wood')

Или

SELECT s.name FROM substances s
WHERE exists(SELECT * from relations r, options o 
      WHERE r.substance_id = s.id and r.option_id = o.id and o.name='yellow')
AND   exists(SELECT * from relations r, options o 
      WHERE r.substance_id = s.id and r.option_id = o.id and o.name='wood')

Наличие единой таблицы options с property_id для разделения различных типов опций - не очень хорошая идея, и на самом деле все намного сложнее. Я бы предложил разбить различные виды опций на разные таблицы

MATERIALS(id,name)
COLORS(id,name)

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

SUBSTANCES(id, name, material_id, color_id)

Тогда ваш запрос намного проще

SELECT s.name FROM substances s, materials m, colors c
WHERE s.color_id = c.id AND m.material_id = m.id
AND m.name = 'wood'
AND c.name = 'yellow'

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

0 голосов
/ 19 августа 2010
SELECT s.name
FROM   substances AS s
       LEFT JOIN relations AS r1
         ON s.id = r1.substance_id
       INNER JOIN relations AS r2
         ON r1.substance_id = r2.substance_id
            AND r1.option_id < r2.option_id
       LEFT JOIN options AS o1
         ON o1.id = r1.option_id
       LEFT JOIN options AS o2
         ON o2.id = r2.option_id
WHERE  o1.name = 'yellow'
       AND o2.name = 'wood'  

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

SELECT s.name
FROM   substances AS s
       LEFT JOIN relations AS r1
         ON s.id = r1.substance_id
       INNER JOIN relations AS r2
         ON r1.substance_id = r2.substance_id
            AND r1.option_id < r2.option_id
       LEFT JOIN options AS o1
         ON o1.id = r1.option_id
     AND o1.name = 'wood'
       LEFT JOIN options AS o2
         ON o2.id = r2.option_id
     AND o2.name = 'yellow'
0 голосов
/ 19 августа 2010

Просто из моей головы, вы можете попробовать:

SELECT DISTINCT s.name FROM substances s, relation r 
WHERE r.substance_id = s.id 
      AND r.option_id IN ( 1, 3) 
...