Да, своего рода немой титул, но мне было трудно описать мою проблему. У меня есть следующие таблицы:
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
id | substance_id | option_id
1 | 2 | 1
2 | 2 | 3
3 | 1 | 1
Теперь у меня есть список вариантов и вы хотите знать, какие вещества связаны со всеми этими вариантами. (Например, какие вещества желтые и сделаны из дерева?) Возможно ли это с одним запросом?
Я пытаюсь сделать это в Rails.
(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 должен легко иметь возможность обрабатывать этот последний запрос намного легче, чем первые два.
Просто со своей головы вы можете попробовать:
SELECT DISTINCT s.name FROM substances s, relation r
WHERE r.substance_id = s.id
AND r.option_id IN ( 1, 3)
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'